我有一个“扭曲”的问题...... 假设有一个像
这样的类class MyClass {
public:
MyClass();
~MyClass();
MyClass& operator=(const MyClass& obj);
private:
int* mem;
};
其中基本MyClass
以某种方式mem
(new
调用),~MyClass()
使用mem
运算符释放delete
。
假设操作员=
超载了代码
MyClass& operator=(const MyClass& obj) {
if(this != &obj) {
//allocate memory of "this" with the "new" operator and copy "obj" data;
}
return *this;
}
我的问题基本上是以下......使用以下语句序列
//statement 1 for short
MyClass my_obj = some_obj_of_MyClass;
我猜一切都很好,因为运算符=
分配内存并复制数据,但是具有以下内容
//statement 2 for short
MyClass obj; //allocate memory for "mem"
obj = some_obj_of_MyClass;
如果认为它不正确我提出的实现,因为我不删除早期分配的内存。我可以在赋值运算符块中调用析构函数,但在这种情况下,语句1可能不安全。
那么实现一切的安全方式是什么? 我想这里的问题可能是了解何时调用析构函数或如何正确实现它。
答案 0 :(得分:0)
您的代码违反了三条规则:每当一个类提供自定义
时它可能会提供所有这三个。因为,如果省略其中任何一个,就可以构造出现不良事件的用例。例如,与直觉相反,声明
MyClass my_obj = some_obj_of_MyClass;
不会调用赋值运算符。它调用您没有提供的复制构造函数。因此,使用默认的复制构造函数,它只是复制指针。一旦my_obj
或some_obj_of_MyClass
被破坏,另一个将有一个指向已删除内存区域的指针。
另一方面,陈述
MyClass obj;
obj = some_obj_of_MyClass;
首先默认构造obj
,然后在其上调用赋值运算符。赋值运算符必须以在obj
的每个可能状态下正确工作的方式编写(即,它不得泄漏内存或双重删除内存)。也就是说,如果分配了obj.mem
,则赋值运算符必须重用该内存或取消分配它(可能用新的分配替换它)。如果obj.mem
可以是空指针,则赋值运算符必须包含对此特殊情况的特殊处理。
所以,您的选择基本上是,您是否希望允许mem
指针成为空指针,如果您不允许这样做,您的默认构造函数必须分配一个可以是的虚拟内存区域由赋值运算符释放。
您还可以通过删除未实现的函数来解决三个规则。你的课可以这样声明:
class MyClass {
public:
MyClass();
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass& obj);
~MyClass();
private:
int* mem;
};
这将禁止声明
MyClass my_obj = some_obj_of_MyClass;
强制用户代码使用更详细的
MyClass obj;
obj = some_obj_of_MyClass;
但它使MyClass
的实现更简单。