1、重载与重写的区别:
重写:函数名和参数的类型及个数完全一样的几个函数,当调用时会发生“就近”(当前作用域)覆盖掉远的函数的情况,就是重写,重点”覆盖“!!!
重载:函数名一样,但参数个数或类型不一样时,调用时就匹配其参数个数或类型等”选择“一个最适合的,就是重载。重点”选择“!!!
2、运算符重载:一般对于类类型的对象定义运算,这种叫运算符重载。
去处符重载的定义: 返回类型 operator运算符(参数列表)
一、只能是对已有的运算符进行重载,不能发明新的运算符。
二、不能修改原运算符的优先级和操作数的个数。比如X比+要优先,我们不能更改,X是二元操作,不能改成三元操作。
三、定义中对于字母字符的去处符如new 、delete要与operator至少保持一个空格,对于其它,空格是可选的。
四、不能参与重载的运算符有: :: ?: . 解析* sizeof
五、重载的函数不一定是类的成员函数。
例: class A{ public: .....
bool operator<(const A& another){..................}
.....................
};
A a(3),b(4);
if(a<b)....;//相当于 a.operator<(a)
3、全局运算符函数:除类成员函数可重载外,一般函数也可以重载。
这时,如果运算符重载函数要访问类的私有成员时,可以声明此函数为友元,即可访问私有。但一般这种情况都声明为类的成员重载运算符更为恰当!!!
注意:如果运算符重载函数的第一个操作数是基本类型,则必须声明为“全局运算符函数”!!
另外强调的是,内联函数必须和它的声明保持在一个文件中,如果声明在头文件中,则其它定义也必须在头文件中,不准在源代码cpp中。
4、重载的几种情况:
二元: 类成员: 返回值 operator运算符(右操作数)
一般函数: 返回值 operator运算符(左操作数,右操作数)//左操作数为基本类型时,必须用一般函数
一元: 类成员: 返回值 operator运算符( )
一般函数: 返回值 operator运算符( 操作数 )
可以看出一般函数比类成员要少一个参数。特殊:前置与后置++ --
前置和一元的情况的一样。
后置为了区分前置,多加了一个参数int
后置: 类成员: 返回值 operator运算符(int)
一般函数: 返回值 operator运算符(左操作数,int)
5、赋值运算符重载。
如果没有提供重载的赋值运算符函数来复制类的对象,编译器就会提供默认版本的operator()。
class A{
public:
int a;
A(const A& another) {a=another.a; cout<<"copy"<<endl;}
A(int num) {a=num; cout<<"number"<<endl;}
A& operator=(const A& another){a=another.a; cout<<"="<<endl; }
};
int main(int argc, char *argv[]){
A b(3),c=b;//输出number,,然后再copy,因为这里是"创建“对象并”初始化“对象
c=b;//因为对象C已经存在,只是使用了赋值。
return 0;
}
一、上面返回值用了引用,更加高效。
二、第一个c=b用的copy,第二个用的是=。
注意:拷贝构造函数:是用一个已知对象去“创建”和“初始化”另一个未建立的对象。
赋值运算符重载函数:用一个已知的对象去改写另一个已知的对象。
两者,编译器都会提供一个默认版本。 但两者的适用范围和适用对象都是不一样的,因此上面的b=c分别调用两个不同的函数。
同时上面程序有一个bug,就是没有返回值,因为赋值运算有a=b=C的情况,因此返回值是必须的。
6、赋值运算符应注意:
它总是应该去检查两个对象是否相等。一、如果右值对象为空,左值将为空;二、如果左值与右值已经相等,不再赋值;三、总应该有拷贝构造函数。
因为如果它的成员是指针,如果只是指针复制,将指向同一目标,左值与右值将相互依赖存在而产生 BUG。
因此:如果类的函数在自由存储区中动态分配内存,就总是应该实现副本构造函数、赋值运算符和析构函数。
7、exit(1)是什么意思?
Exit用于在程序运行的过程中随时结束程序,函数原型void exit(int status),exit的参数是返回给操作系统的。 这个参数识了应用程序的一些运行信息。 和操作系统有关一般是0为正常退出,非0为非正常退出。 非0的数字可以是自己拟定的可以是任意一个整型数字。 退出进程,会根据其返回的int值做善后处理 。
8、经济化运算符的重载。
有时我们可以根据前面的重载,在另一个重载中调用前面的,达到经济节约代码的效果:
class A{
int a;
public:
A(int m=1){a=m;}
void show(){cout<<a<<endl;}
int getvalue()const{return a;}
A& operator +=(const A& another){
a+=another.getvalue();
return *this;
}
A& operator +(const A& another)const {
return A(*this)+=another;//返回的是临时值,因为本身就是中间值
}
A& operator =(const A& another){
a=another.getvalue();
return *this;
}
};
int main(int argc, char *argv[]){
A a(3),b(4),c;
c=a+b;
a.show();// 查看是否修改了a,结果:没有修改。
return 0;
}
可以看到+中调用了+=,节约了代码。同样>和<、*和*=,/和/=、-和-=等等,可以相互利用一下。
9、operator [](int value) 下标的重载。数组的重载,因为有些是链表,有时有必要进行“元素化”成数组。
注意两点:
一、lvalue(左值)与重载下标的混合:有时有load[0]=load[2];
如果[]重载的返回值是临时对象的话,作为左值就会出问题。这里返回值用引用(不能是临时变量的引用)可作为左值。
二、const的重载:
有时,对象可作左值,有时又不能作左值,如何满足这两种 情况。
用返回参数的性质来改变,一个用const参数,一个用非const参数,这样就可以重载了。
但这两个在一个类中是没法共存的。。。。
10、类型转换运算符重载
一个类转换为另一个类型,针对类类型转为其它类型: operator Type();
注意:1、源对象是本类类型;2、目标(返回值类型)不用写,始终是type;3、有返回值。
当Box类对象theBox有下面时: operator double() const;//定义转换类型重载,返回double类型
double a=theBox;//theBox对象会隐式地转换成double型,然后给a赋值。
上面相当于 double a=static_cast<double>(theBox);
或: double(theBox);
构造函数成有转换功能: A(const B& another);//把another转换到A类型
operator B(); //把A类型转换到B类型
有时,转换类型时编译器不知道选择哪个函数,为了去除转换运算符和构造函数之间的这种模糊性,可以把构造函数声明为explicit(显式)。
11、递增和递减重载。
对于前置就是前面用的默认即可,不过这个一般返回的是引用。
对于后置,须构造一个对象副本保存原对象,然后原对象递增或递减,再返回那个保存原值的副本即可。因此,它返回的是对象。
12、智能指针,又称迭代器。
它实质上是一个类构造的一个对象,只不过个这对象有点特殊,它重载了指针的一些功能,形式上象指针一样使用,事实上象指针一样地操作。
就象一个类重载了+,但实际上内部的代码用的是减法的代码,因此,它完成的是减法的功能,智能指针一样,实质是对象,但重载中完成了指针的功能。
因此,它一会是对象在用,一会又象是作指针在用,感觉很智能。
看一个例子:TruckLoad是一个链表类,这个链表有节点,节点类是Package,每个节点有数据Box类,和下一个节点的指向即Package*.
为此,我们定义一个Box智能指针类,它可作Box对象,也可作为Box指针来使用
class BoxPtr{
public:
BoxPtr(TruckLoad& load){pBox = rLoad.getFirstBox();}//构造只能由TruckLoad来产生
Box& operator*()const{ //重载解除指针,返回应该是对象,为了高效返回引用
if(pBox)
return *pBox;
else{
cout << endl << "Dereferencing null BoxPtr";
exit(1);
}
}
Box* operator->()const{ return pBox;}//重载->,返回一个指针
Box* operator++(){ return pBox = rLoad.getNextBox();}//重载++,返回下一个Box的指针
const Box* operator++(int){ //重载后置++,先下移,返回原指针副本
Box* pTemp = pBox;
pBox = rLoad.getNextBox();
return pTemp;
}
operator bool(){ return pBox != 0;}//重载bool,以便if中判断
private:
Box* pBox;
TruckLoad& rLoad;
//防止其它来构造,放入私有表示禁止
BoxPtr();
BoxPtr(BoxPtr&);
BoxPtr operator=(const BoxPtr&);
};
可以看到,这个类可以作为Box类来使用,也可当Box的对象的指针来使用。它们的重载都是返回的指针。把构造函数放进私有可以防止其它调用来构造。
它只能由TruckLoad类来构造。
operator->()的重载:BoxPtr pLoadBox(aTruckLoad);
double boxVol=pLoadBox->volume();//形式上象是使用了指针,这里重载了->,等价于:boxVol=(pLoadBox.operator->())->volume();
返回值是const防止重复重载如pLoadBox++++,另外,bool重载是方便使用 if(pLoadBox){....}判断的情形。
13、 引用临时对象是个错误,特别是通过地址引用临时对象的错误,是因为临时对象的生命结束在引用临时对象前