我们需要注意的是,当我们不写等号运算符重载的时候,系统会默认生成浅赋值形式等号运算符重载,所谓的浅赋值就是将目标对象的成员属性直接赋值给当前对象(可以参考深拷贝和浅拷贝,在我的“C++——拷贝构造函数详解”这篇文章中有详细讲解)。
Object
{
public:
//其他的构造函数、析构函数不是本篇文章重点,省略。
void operator=(const Object& obj)
{
this->value=obj.value;
}
/*
在主函数中使用obja=objb=objc,等价于下面的方法调用:
obja=objb.operator=(objc);
obja=operator=(&objb,objc);
*/
private:
int value;
}
对于上述的赋值语句需要说明以下三点:
①这个赋值语句不能用const进行修饰,因为this->value在赋值的过程中有所改变
②这个方法不能实现连续赋值,因为返回值为void,可以将void改成Object,就能实现连续赋值
③由于对象在主函数中,通过引用传参进入当前方法,因此obj对象不受这个重载函数调用期的影响,所以可以用引用进行返回
不写等号运算符重载有两种情况:
①没有自己设计的类型和对象,都是基本类型,那么系统就会将两个对象的成员属性地址抓住,直接目标对象成员属性的数据导入当前对象成员属性中。
②如果成员属性中不止是基本类型,那么如果我们不写等号运算符重载,系统就会产生默认的浅赋值语句。
class Int
{
private:
int value;
public:
//前置加加
Int& operator++()
{
this->value+=1;
return *this;
}
//后置加加
Int operator++(int)
{
Int tmp = *this;
++* this;//调用前置加加
return tmp;
}
//对象+对象
Int operator+(Int& b)const
{
Int c;
c.value = this->value + b.value;
return c;
}
//对象+数值
Int operator+(int val)const
{
Int c;
c.value = this->value + val;
return c;
}
};
//数值+对象
Int operator+(const int val, const Int& a)
{
return a + val;//调用Int operator+(int val)const 这个方法
}
class Int
{
private:
int value;
public:
//省略构造函数、等号运算符重载,这里主要说明取地址运算符重载
Int* operator&()
{
return this;
}
const Int* operator&()const//加了const的取地址运算符重载是一个常方法
{
return this;
}
}
int main()
{
Int a(10);
Int b=10;//这句话会先调用构造函数构造一个value成员属性为10的对象,然后把这个对象赋值给b
const Int c=10;
const Int *ip=&c;//因为C是常对象,所以就会调用上面的常方法
}
因为是后置++,通过下面的代码可知,我们需要创建一个对象用来保存原来的值,然后再将当前值+1.然而创建的这个对象的生存期和重写后置++方法的生存期一样,当函数调用结束,空间就会被释放。所以不可以返回引用。
//后置加加
Int operator++(int)
{
Int tmp = *this;
++* this;//调用前置加加
return tmp;
}
但是如果我们非要返回引用,加一个static可不可以呢?如下:
Int& operator++(int)
{
static Int old=*this;
++* this;
return old;
}
上述代码因为定义成静态,那么当我们执行一次后置++没有问题,但是因为是静态,只执行一次 static Int old=*this;,当我们再次执行后置++的时候,值并不变,还是之前的值,那么我们改成下面的:
Int& operator++(int)
{
static Int old=*this;
old=*this;
++* this;
return old;
}
加上old=*this这句话之后,那么我们每执行一次后置加加方法,就会将静态变量old重新赋值给当前的对象,从而避免上述的“再次执行后置++的时候,值并不变,还是之前的值”的问题。
举例对修改之后的代码作以说明:
最后d输出12,因为有 old=*this;会更新old。
结合下面的运算符重载,分析Int a=0;a=a++ +1;这句代码。
(1)隐式的类型转化:
所谓的隐式类型转化通过下面的例子说明:
a=b,这句话其实是先用整型b的值100进行构造,将构造的这个对象给a赋值,主函数调用结束之后析构这个对象,析构a。
(不允许隐式转换的关键字:explicit)
(2)强制类型转换:
把对象强转成某一个内置类型
//把对象强转成整型
operator int() const
{
return value;
}
强转返回的类型就是强转之后的类型,因此重载函数不需要设置返回类型
例如下面呢的:如果返回flot,就和返回类型冲突。
class Add
{
mutable int value;//加了mutable这个关键字之后,可以让value这个变量在常方法中也能改变
public:
Add(int x=0):value(x){};
int operator()(int a,int b)const//括号运算符重载
{
value=a+b;
return value;
}
}
int main()
{
int a=10,b=20,c=0;
Add add;
c=add(a,b);//调用括号运算符重载,我们也称之为仿函数
//c=add.operator()(a,b);
return 0;
}
下面的输出运算符重载是错误的。必须通过引用来调用out参数,因为系统的代码设置的是私有的输出流,所以必须是引用调用,如果不是引用调用,就会调用拷贝构造,而拷贝构造被设置为私有,无法调用。
class String
{
private:
char * str;
public:
//ostream& operator<<(const String* const this,ostream& out)
ostream& operator<<(ostream out)const
{
if(str!=NULL)
{
out<<str;
}
return out;
}
//s1
//s1.operator<<(cout)
//operator(&s1,cout)
}
正确的如下:
把str的内容写入搭配out中,并返回
但是上述的输出运算符重载在使用的时候是:s1<
先调用ostream& operator<<(ostream& out,const String& s)
然后再对象内部调用输出运算符重载
class String
{
private:
char * str;
public:
//ostream& operator<<(const String* const this,ostream& out)
ostream& operator<<(ostream& out)const
{
if(str!=NULL)
{
out<<str;
}
return out;
}
//s1
//s1.operator<<(cout)
//operator(&s1,cout)
}
ostream& operator<<(ostream& out,const String& s)
{
s<<out;
//s.operator<<(out);
//operator<<(&s,out);
return out;
}
int main()
{
String s1("hello world");
cout<<s1<<endl<<;
return 0;
}
class Int
{
private:
int value;
};
class Object
{
Int* ip;//Int是一个类
public:
Object(Int* s=NULL):ip(s){}
~Object()
{
if(ip!=NULL)
{
delete ip;
}
ip=NULL;
}
//✳运算符重载返回这个对象所指向的地址
Int* operator*()
{
return ip;
}
const Int* operator*()const
{
return ip;
}
//✳运算符重载返回ip所指向的对象
Int& operator*()
{
return *ip;
}
const Int& operator*()const
{
return *ip;
}
};
int main()
{
Object obj(new Int(10));
(*obj)->value();
}
class Int
{
private:
int value;
};
class Object
{
Int* ip;//Int是一个类
public:
Object(Int* s=NULL):ip(s){}
~Object()
{
if(ip!=NULL)
{
delete ip;
}
ip=NULL;
}
//返回所指向的这个对象的地址
const Int* operator->()const
{
return ip;
}
}
int main()
{
Object obj(new Int(10));
obj->value();
}
注:通过星号运算符重载和指向运算符重载综合运用来实现“对象的使用自由化”问题:
问题引入:
class Int
{
private:
int value;
};
class Object
{
Int* ip;//Int是一个类
public:
Object(Int* s=NULL):ip(s){}
~Object()
{
if(ip!=NULL)
{
delete ip;
}
ip=NULL;
}
Int& operator*()//重载*,返回ip所指对象本身
{
return *ip;
}
const Int& operator*()const
{
return *ip;
}
Int* operator->()
{
return &**this;
}
const Int* operator->()const
{
return &**this;
//return ip
}
}
上面的的&**this表示什么呢?
对于上述代码我们结合下图从右往左进行分析:this就是指向obj这个对象,*this表示obj这个对象,然后**this就调用了✳运算符重载,表示ip所指向的这个对象本身,所以这个合起来表示ip所指向的这个对象的地址
所以进✳运算符和->运算符的重在之后,也能够得到Int对象,并且利用Obj这种形式实现了自由化,也就是说在析构的时候就会自动析构ip所指向堆区的空间,不用我们手动释放。