c++对象的赋值
c++中是允许将对象的值赋给自己的。5 };a = a;编译器并不会阻止这种自我赋值的行为,在MyClass类中,因为只有一个int类型的数据成员,所以自我赋值并没有过多问题,但当类具有动态分配的内存或者其它资源时,必须将自我赋值考虑在内。所以为了阻止自我赋值的行为,我们需要做些检测,如果发现是自我赋值,那么立马返回。判断两个对象是否相同的方法之一是检查它们在内存中的位置是否相同,换句话说就是检查
c++对象的赋值
就像可以将一个int变量赋值给另外一个int变量一样,在c++中也可以将一个对象的值赋给另一个对象。假设有类MyClass:
MyClass a{1}, b;
b = a;
对象a就像复制给了对象b一样。然而在c++中,“复制"只在初始化对象时发生,如果一个已经具有值的对象被改写,更精准的说法叫做"赋值”。在c++中提供的复制工具是拷贝构造函数。因为这是一个构造函数,所以只能用在创建对象的时候,而不能用于对象的赋值。
因此,c++为所有的类提供了执行赋值的方法。这个方法被成为赋值运算符(assignment operator),名称是operator=,实际上是重载了=运算符。
tips: 赋值运算符有时候也称为拷贝赋值运算符(copy assignment operator),因为在赋值后,左边和右边的对象都继续存在。
如果没有自己编写赋值运算符,编译器将自动生成一个。默认的c++赋值行为几乎与默认的复制行为相同,以递归的方式用源对象的每个数据成员赋值给目标对象。
赋值运算符的声明
下面展示MyClass类的赋值运算符:
class MyClass
{
public:
MyClass() {} // 无参构造函数
MyClass(int a) : m_a{a} {} // 有参构造函数
MyClass(const MyClass &src){} // 拷贝构造函数
MyClass &operator=(const MyClass &rhs){} // 赋值运算符
~MyClass() {} // 析构函数
private:
int m_a;
};
赋值运算符与拷贝构造函数类似,采用了源对象const引用。 在此情况下,将源对象称为rhs(right hand side),代表等号的"右边"(可以指定其它名称),调用赋值运算符在等等的左边。
与拷贝构造函数不同的是,赋值运算符返回MyClass对象的引用。原因是赋值可以链接在一起,例如:
a = b = c;
上述连等完整的语法是:
a.operator=(b.operator=(c)); // 对象c先赋值给对象b,b再赋值给a
tips: 实际上可使赋值运算符返回任意类型,包括void。但是由于它的功能是赋值,所以返回被调用对象的引用是最明智的。
赋值运算符的定义
c++中是允许将对象的值赋给自己的。例如下面的代码是可以编译通过的:
MyClass a{5};
a = a;
编译器并不会阻止这种自我赋值的行为,在MyClass类中,因为只有一个int类型的数据成员,所以自我赋值并没有过多问题,但当类具有动态分配的内存或者其它资源时,必须将自我赋值考虑在内。所以为了阻止自我赋值的行为,我们需要做些检测,如果发现是自我赋值,那么立马返回。
判断两个对象是否相同的方法之一是检查它们在内存中的位置是否相同,换句话说就是检查指向它们的指针是否相同。当一个非静态对象被创建时,指向对象数据的首地址是this指针。赋值运算符参数中的&rhs指向赋值过来的对象。那么当等号左边的this指针等于等号右边的&rhs指针,就说明是对象的自赋值。由于返回类型是MyClass& ,所以必须返回一个正确的值。所有的赋值运算符都返回*this(对象),那么自我赋值也应该返回 *this。如果判断不是自我赋值,那么就需要给每一个成员都赋值:
MyClass &operator=(const MyClass &rhs)
{
if (this == &rhs) // 自赋值判断
{
return *this;
}
else
{
m_a = rhs.m_a;
return *this;
}
}
显示默认或者显示删除赋值运算符
赋值运算符也可以使用default和delete关键字可显示的或者删除编译器生成的赋值运算符。
MyClass &operator=(const MyClass &rhs) = default; // 使用编译器默认生成的
MyClass &operator=(const MyClass &rhs) = delete; // 删除编译器生成的
区分复制和赋值
有时候很难区分对象何时用拷贝构造函数初始化,何时用赋值运算符赋值。基本上,声明时会使用拷贝构造函数,赋值语句会使用赋值运算符。例如:想一想下面哪些是执行拷贝构造函数?哪些是执行赋值运算符?
MyClass a{1};
MyClass b{a};
MyClass c = a;
b = c;
答案是:第2、3行执行的是拷贝构造函数,第4行执行的赋值运算符,小朋友你猜对了嘛?
好的,既然你现在掌握了区分复制和赋值的要领了,那么再猜一猜下面拷贝构造函数的实现分别调用的什么函数?
MyClass(const MyClass &src) : m_a{src.m_a} {};
MyClass(const MyClass &src) { m_a = src.m_a; }
答案是:第一行调用的是拷贝构造函数,第二行调用的是赋值运算符函数!没答对的小朋友仔细从头学习本篇内容哦!
更多推荐
所有评论(0)