.h文件
classmyclass
{
public:
operator int () const {return m_i;} //转换操作符
friend class otherclass; //友元
myclass():m_i(0), m_i(0.5), m_i2(0){m_i = 0; …} /*初始化,无返回值,不能声明为const,初始化列表一定会先于函数体执行,无论显示或隐式初始化,此时成员变量已经有值.如果没有对成员变量显示初始化,编译器会隐式地使用成员类型的默认构造函数,内置类型无默认构造函数,则编译器尝试隐式初始化失败,此时必须显示初始化或在函数体中赋值.无默认构造函数的类类型成员,以及const或者引用类型成员,必须在构造函数初始化列表中初始化.成员初始化顺序是定义成员的顺序.如果无构造函数,编译器会生成默认构造函数,如果有显示定义构造函数,则编译器不会生成默认构造函数.*/
myclass(int i):m_i2(0),m_i(i){}//可被重载
bool inlinefunc() {...} //inline函数
inline void func1(); //inline函数,可以在声明中引用inline,也可在定义中引用
const myclass& func2() const; /*普通成员函数,返回调用对象的引用,const成员函数必须返回const对象的引用.const对象只能使用const func2()成员函数.非const对象也可调用.*/
myclass func2(int i); //重载函数
myclass& func2(); /*重载函数,非const对象可以调用,非const对象调用fun2()函数时,非const func2()成员函数优先级大于const func2()成员函数.*/
typedef int MYINT;
MYINT func3(MYINT i);
bool equal(myclass m);
static void staticfunc(void);
virtual void virtualfunc(void);
virtual void virtualfunc2(void);
public:
int m_i2;
private:
const int m_i; //const成员变量,必须在构造函数初始化列表中初始化.
static float m_f; //static 成员变量
myclass *m_class; //前向声明
mutablechar c; //可变数据成员,在const成员函数中也可以修改.
static myclass m_static_class; //static可以是该成员所属的类类型.
//myclass m_class; //error
};
void myclass::func1(){…}
.cpp文件
const myclass& myclass::func2()const {...return *this;}
myclass myclass::func2(int i){…}
myclass& myclass::func2(){…}
myclass::MYINT myclass::func3(MYINT i){ MYINT a; …} /*返回值类型定义在类定义体内,则必须使用完全限定名,因为返回值类型出现在函数名之前,此时代码不在类作用域内.而对于形参和成员函数体出现在成员名之后,这些都是在类作用域中定义,所以不用限定. */
bool myclass::equal(myclass m){return (m.m_i == m_i);}
struct默认成员为public, class默认成员为private.
定义class类型时,并未分配内存.
classmyclass; 之后需要定义,在定义之前,myclass是一个不完全类型,此时不能定义该类型对象,只能用于定义指向该类型的指针和引用,或者用于声明(不是定义)使用该类型作为形参或返回类型的函数.在创建类对象之前,必须完整地定义该类,这样编译器就会给类的对象预定相应的存储空间.
类成员不能为自身类型,但是可以有指向该类型的指针和引用,因为此时已经声明了,但是未定义.
成员函数具有一个附加的隐含形参,即指向该类的this指针,成员函数不能定义this形参,只能由编译器隐含地定义,成员函数不必显示地使用this指针,编译器会自动将类成员的引用处理成通过this指针的引用.
成员函数返回对调用该函数的对象的引用时,需要显示地使用this指针.(return *this)
如果类定义体中 //myclass():m_i(0), m_i(0.5){m_i = 0; …}
myclass mine();//不是使用默认构造函数初始化对象, 被编译器解释为一个函数的声明.
mine.func2(); //无法编译,
正确的做法:
myclass mine; //使用默认够找函数定义对象,去掉最后的括号.
mine.func2();
myclass mine;
mine.equal(3); //隐式调用myclass(int i),生成临时对象,
如果希望抑制隐式类类型转换,则可将构造函数声明为explicit.
explicit myclass(int i):m_i2(0),m_i(i){}
可以直接引用myclass的私有成员,可以声明在类中任何地方.
class otherclass { public: void otherfunc(myclass mine) { int i = mine.m_i; //可以访问私有成员 } };
可以将其他类的某个成员函数或者普通非成员函数定义为友元.
#include "otherclass.h"//必须包含友元类的定义. classmyclass { friend void otherclass::otherfunc(myclass mine); };
otherclass.h
class myclass; class otherclass { public: void otherfunc(myclass mine); };
otherclass.cpp
void otherclass::otherfunc(myclass mine) { int i = mine.m_i; }
也可如下:
class myclass { friend class otherclass; friend void otherfunc(myclass mine) { int i = mine.m_i; } };
otherclass.h
class myclass; class otherclass { public: void otherfunc(myclass mine); };
static 成员变量独立于类的任意对象,非const static必须在类定义体外部定义并初始化(类定义体中只是声明),不能通过构造函数初始化.static关键字只能用于声明中,定义不能标识为static.
const static可以在类定义体重初始化.
static成员函数无this形参,不可使用非staic成员变量,不可声明为const.
使用域操作符可以直接调用static成员.
static成员类型可以是该成员所属的类类型: static myclass m_static_class;
string a("123"); //直接初始化
string b = "123"; //复制初始化,首先用构造函数创建一个临时对象,再用复制构造函数将临时对象复制到创建的对象. = 也可以调用复制构造函数.
vector<string> svec(5); //使用string默认构造函数创建一个临时值来初始化svec,再使用复制构造函数将临时值复制到svec的每个元素.
编译器会合成一个复制构造函数,即使定义了其他构造函数.一般不设为explicit
myclass(const myclass& other):m_i(other.m_i)… {…} //形参为const引用
复制每一个成员.
禁止复制: 显示声明复制构造函数为private且不定义.不允许复制的类对象只能作为引用传递或者返回,也不能作为容器元素.如果定义了复制构造函数,也必须定义默认构造函数.
class& operator=( constclass &) { …}
有两个形参,第一个形参独赢作操作数,第二个形参对应右操作数,
第一个操作数隐式绑定左操作数的this指针,又操作数一般为const引用传递
如果无显示定义赋值操作如,则编译器隐式合成赋值操作符.
合成析构函数按对象创建时的逆序撤销每个非static成员.
析构函数无返回值,无形参.
即使运行自己的析构函数,合成析构函数仍然运行.
操作符定义为非成员函数时(比如模板),通常必须将他们设置为所操作类的友元,因为通常需要访问私有成员.
不用重载具有内置含义的操作符,比如赋值操作符,取地址操作符,逗号操作符,&&, ||,这些操作符具有内置含义,如果定义自己的版本,则不能使用这些内置含义.
输出操作符<<的重载
ostream& operator <<(ostream& os, const clastype& object) { os<<...; return os; }
I/O操作符必须为非成员函数,否则,左操作数必须是该类类型对象: object << os,与正常使用方式相反.
输入操作符>>的重载
instream& operator <<( instream & in, clastype& object) { in>…; return in; }
算术操作符和关系操作符+, -等
classtype operator+( const clastype& object1, const clastype& object2);
相等操作符 == !=
最好定义为非成员函数.
关系操作符 > <
下标操作符[]
两个版本: const和非const
returntype & operator[](const size_t);
constreturntype & operator[](const size_t)const;
自增自减操作符 ++ --
returntype & operator++(); //前缀
returntype & operator--(); //前缀
returntype operator++(int); //后缀
returntype operator--(int); //后缀
第一参数为隐式的this指针
后缀自增自减操作符接受一个额外的无用的int型形参,编译器提供0作为这个形参的实参.
显示调用:
object.operator++();
object.operator++(0);
struct absInt
{
int operator() (int val) //函数调用操作符
{
return val < 0 ? -val : val;
}
}
int i = -21;
absInt absobj;
int a = absobj(i); /*运行absobj对象定义的重载调用操作符.int a = absobj.operator ()(i); */
总之操作符第一个参数为左操作数, 如果有第二个参数,则为左操作数.
如果定义在类定义体中,则第一个参数为隐式的this指针,显示调用操作符用operator操作符(),或者直接调用操作符.
例如:
bool GT6(const string &s) { return s.size > = 6; } vector<string> word; vector<string>::size_type wc = count_if(word.begin(), word.end(), GT6);
更灵活的写法:
class GT_cls { public: GT_cls(size_t val = 0): bound(val){} bool operator()(const string &s) { return s.size() >= bound; } private: string::size_type bound; } vector<string> word; vector<string>::size_type wc = count_if(word.begin(), word.end(), GT_cls(6));
GT_cls(6)创建临时对象,之后每次调用函数调用操作符().
标准库定义的函数对象
标准库定义了一组算术,关系与逻辑函数对象类,这些类型是在functional.h中定义
每个函数对象类都是类模板,函数对象类的模板类型指定调用操作符的形参类型,
除了negate<Type> 和logical_not<Type>是一元函数对象,其余都是二元函数对象(需要两个形参).
算术函数对象类型
plus<Type> +
minus<Type> -
multiplies<Type> *
divides<Type> /
modulus<Type> %
negate<Type> -
关系函数对象类型
equal_to<Type> ==
not_equal_to<Type> !=
greater<Type> >
greater_equal<Type> >=
less<Type> <
less_equal<Type> <=
逻辑函数对象类型
logical_and<Type> &&
logical_or<Type> ||
logical_not<Type> !
plus<int> intAdd;
int sum = intAdd(10, 20); //30,使用intAdd::operator(int, int)
sort(word.begin(), word.end(), greater<string>());
标准库提供了一组函数适配器,用于扩展和特化一元和二元函数对象.
绑定期(blinder): 通过将一个操作数绑定到给定值而将二元函数对象转换为一元函数对象,blind1st和blind2nd,blind1st将给定值绑定到二元参数对象的第一个实参,blind2nd将给定值绑定到二元参数对象的第二个实参.
count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 10));
求反器(negator): 将谓词函数对象的真值求反. not1和not2, not1将一元函数对象的真值求反, not2将二元函数对象的真值求反.
count_if(vec.begin(), vec.end(), not2(bind2nd(less_equal<int>(), 10)));
此时less_equal<int>是一元操作.
class tempclass
{
public:
tempclass(int i = 0): m_i(i), m_f(0){}
tempclass(double): m_i(0), m_f(f){}
tempclass(tempclass2): m_i(obj.m_i), m_f(obj.m_f){}
operator double() const { return m_f;}
operator int() const {return m_i;}
private:
int m_i;
double m_f;
};
class tempclass2
{
public:
tempclass2(int i = 0): m_i(i), m_f(0){}
tempclass2(double): m_i(0), m_f(f){}
operator double() const { return m_f;}
operator int() const {return m_i;}
operator tempclass () const { tempclass obj, … returnobj;}
private:
int m_i;
double m_f;
};
转换操作符在类定义体内声明,在保留字operator之后跟着转换的目标类型:
operator type(); //可以将类对象转换为type类型对象.
一般为const, 且类定义中只有一个类类型转换.
只允许一次类类型转换,比如A->B->C,在需要C的地方,不能赋予A.
但是标准转换可放在类类型前,比如float->int,之后再通过类类型转换,也可放在之后.
void cals(tempclass);
short i;
calc(i); //i标准转换为int,之后再类类型转换为tempclass类型.
void cals(short i);
tempclass obj;
calc(obj); //再类类型转换为int类型,之后int标准转换为short.
具有二义性(ambiguous)时,转换失败.
void func(long double);
tempclass obj;
func(obj); //ambiguous
void func(tempclass);
long double f;
tempfunc(f); //ambiguous
void func(tempclass);
tempclass2 obj;
func(obj); /*ambiguous, 既可调用tempclass中的构造函数tempclass(tempclass2),也可以调用tempclass2中的类类型转换操作operator tempclass () const; */
可以显示地调用
func(tempclass (obj)); //构造函数
func(obj.operator tempclass ()); //类类型转换
如果
class tempclass
{
public:
tempclass(tempclass2& obj): m_i(obj.m_i), m_f(obj.m_f){}
}
则上述不具有二义性, 因为tempclass需要将一个引用绑定到obj,而用类类型转换可以省略这个步骤,类类型转换具有优先性.
void func(long doublef);
void func(int i);
void func(doublef);
void func(tempclass obj)
tempclass obj;
func(obj); //ambiguous, 上述四个函数,无论哪两个在一起,都具有二义性.
可以显示调用
tempclass obj;
func(obj.operator int());
tempclass obj;
func(static_cast<int>(obj));
void func(tempclass2 obj);
void func(tempclass obj);
inti = 0;
func(i); //ambiguous
可以显示调用
func(tempclass(i));
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?