Operator=

本文探讨了C++中赋值构造函数的几个关键问题,包括为何返回类型是类本身、如何避免自我赋值引发的问题,以及在自定义赋值构造函数时需要考虑的数据成员。通过一个具体的字符串类实例,详细解释了每个问题的原因和解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[align=center][size=large][b]Operator=[/b][/size][/align]

赋值构造函数,问题1:为何返回类型是类,即return *this;
问题2:为何要对自身判断,怎么判断以防止赋值给自己?
问题3:自己定义赋值构造函数,要对那些数据赋值?

带着这些问题,看看下面的例子:

[b]问题1:为何返回类型是类,即return *this;[/b]


#include <iostream>
using namespace std;

class String {
public:
String();
String(const char *value); // 函数定义参见条款11
~String(); // 函数定义参见条款11

void operator=(const String& rhs);
void Print() const;

private:
char *data;
};

String::String( const char *value )
{
if (value) {
data = new char[strlen(value) + 1];
strcpy(data, value);
}
else {
data = new char[1];
*data = '\0';
}

}

String::String()
{
data = new char[1];
*data = '\0';
}


String::~String()
{
//delete [] data;
}


void String::operator=( const String& rhs )
{
delete [] data;
//allocate new memory
data = new char[sizeof(rhs.data)+1];
strcpy(data,rhs.data);

//return *this;
}

void String::Print() const
{
cout << "The content is :" << data << endl;
}


int main()
{
String a = "hello";
String b = "world";
String c = "hewei";
a.Print();
c = b = a;
b.Print();
return 0;
}


这个例子可以通过编译,但是不能运行,原因何在?

此赋值构造函数为:
void operator=(const String& rhs);
注意返回类型为void,那么c = b = a;就会出错。

因为c = b = a;等价于c.operator=(b.operator=(a.operator=("hello"))); operator=结合性由右向左,a.operator=("hello")结果是:a.data = "hello";但是a.operator=("hello")经过赋值构造函数后并没有返回值,就无法在继续赋值给b。

因此赋值构造函数必需有返回值,且返回值为类本身的引用。

其正确的赋值构造函数写法如下:

String& operator=(const String& rhs);
String& String::operator=( const String& rhs )
{
if(this == &rhs)
return *this;
delete [] data;
//allocate new memory
data = new char[sizeof(rhs.data)+1];
strcpy(data,rhs.data);

return *this;
}


[b]问题2:为何要对自身判断,怎么判断以防止赋值给自己?[/b]

上面的例子:若赋值构造函数为:

String& operator=(const String& rhs);
String& String::operator=( const String& rhs )
{
//if(this == &rhs)
// return *this;
delete [] data;
//allocate new memory
data = new char[sizeof(rhs.data)+1];
strcpy(data,rhs.data);

return *this;
}


当执行到语句:a = a;就会出现问题:
*this data ------------> "hello\0"
/
/
rhs data -----

赋值运算符做的第一件事是用delete删除data,其结果将如下所示:

*this data ------------> ???
/
/
rhs data -----

现在,当赋值运算符对rhs.data调用strlen时,结果将无法确定。这是因为data被

删除的时候rhs.data也被删除了,data,this->data 和rhs.data 其实都是同一个

指针!从这一点看,情况只会越变越糟糕。

现在可以知道,解决问题的方案是对可能发生的自己给自己赋值的情况先进行检查

,如果有这种情况就立即返回.

[color=red]问题是如何判断两个对象“相同”??[/color]

确定对象身份是否相同的方法是用内存地址。采用这个定义,两个对象当且

仅当它们具有相同的地址时才是相同的。这个定义在c++程序中运用更广泛,可能

是因为它很容易实现而且计算很快。

其判断形势为:
if(this == &rhs)
return *this;

[b]问题3:自己定义赋值构造函数,要对那些数据赋值?[/b]
### 关于编程中的 `operator=` 使用或错误 `operator=` 是 C++ 中的一个特殊成员函数,用于实现对象赋值操作。它允许开发者定义如何将一个对象的内容复制到另一个对象中。如果未显式定义该运算符,则编译器会自动生成默认版本[^3]。 #### 默认行为 当类没有提供自己的 `operator=` 实现时,C++ 编译器会生成一个默认的拷贝赋值运算符。这个默认版本逐成员地执行浅拷贝(shallow copy)。这意味着对于指针类型的成员变量,仅复制其地址而非实际数据内容[^4]。 ```cpp class MyClass { public: int* ptr; // Default constructor MyClass() : ptr(new int(0)) {} ~MyClass() { delete ptr; } }; // Example of shallow copying leading to undefined behavior. void testDefaultAssignmentOperator() { MyClass obj1; *obj1.ptr = 10; MyClass obj2; obj2 = obj1; // Uses default assignment operator std::cout << "*obj2.ptr: " << *obj2.ptr << "\n"; // Outputs 10 // Both objects now point to same memory location after destruction leads to double-free error } ``` 上述代码展示了由于使用默认赋值运算符而导致的双重释放问题[^5]。 #### 自定义 `operator=` 为了避免潜在的风险,在某些情况下需要重载 `operator=` 来实施深拷贝(deep copy),即创建新内存并复制原始数据至其中: ```cpp class MyClassWithDeepCopy { private: int* data; public: explicit MyClassWithDeepCopy(int value) : data(new int(value)) {} ~MyClassWithDeepCopy() { delete data; } // Copy Assignment Operator Overload using copy-and-swap idiom MyClassWithDeepCopy& operator=(const MyClassWithDeepCopy& other) { auto temp = new int(*other.data); swap(data, temp); delete temp; return *this; } }; ``` 此方法遵循了 **copy-and-swap** 范式来确保强异常安全性以及自我赋值的安全处理[^6]。 #### 常见错误及其解决方案 以下是几个常见的关于 `operator=` 的误用场景及对应的修正措施: 1. **忽略返回类型** 如果忘记让 `operator=` 返回当前实例 (`return *this`) ,则链式调用将会失效。 2. **缺乏对自赋值的支持** 当目标和源是同一个对象时可能发生意外情况;因此应始终考虑这种情况下的正确性保障。 3. **资源泄漏风险** 对动态分配的数据结构进行管理不当容易引发内存泄露等问题,故需谨慎对待每一步骤以防止此类隐患发生。 #### 并发环境下的注意事项 在多线程环境中应用 `operator=` 可能面临额外挑战。例如,两个不同线程可能同时尝试修改同一共享状态的对象副本。此时应当引入同步机制保护关键区域免受竞争条件影响[^7]: ```cpp std::mutex mtx; class ThreadSafeObject { protected: mutable std::mutex m_mutex; int value_; public: ThreadSafeObject(const ThreadSafeObject &rhs) noexcept(false){ std::lock_guard<std::mutex> lock(rhs.m_mutex); this->value_ = rhs.value_; }; ThreadSafeObject& operator=(ThreadSafeObject const& other) { if(this != &other){ std::unique_lock<std::mutex> lhsLock(m_mutex,std::defer_lock), rhsLock(other.m_mutex,std::defer_lock); std::lock(lhsLock,rhsLock); // Avoid deadlock value_=other.value_; } return *this; }; }; ``` 通过以上方式可以有效减少因并发访问带来的不确定性因素干扰程序正常运行流程的可能性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值