不允许赋值和传递值
Disallowing assignment and passing by value
据我所知,我可以通过定义私有复制构造函数和赋值运算符来"禁用"复制和赋值给我的对象:
class MyClass
{
private:
MyClass(const MyClass& srcMyClass);
MyClass& operator=(const MyClass& srcMyClass);
}
但是这个有什么用呢
这被认为是一种糟糕的做法吗?
如果你能描述一下这种情况,我将不胜感激,在这种情况下,以这种方式"禁用"赋值和复制构造函数是合理/有用的。
当复制对象没有意义时,它很有用。这绝对不是一种坏做法。
例如,如果您有一个表示网络连接的类,那么复制该对象是没有意义的。另一种情况是,如果你有一个类在多人游戏中代表一个玩家,那么你可能希望一个类是不可复制的。这两个类都代表了在现实世界中无法复制的东西,或者复制起来没有意义的东西(一个人,一个连接)。
此外,如果您正在尝试实现Singleton,那么使对象不可复制是标准过程。
一般来说,任何管理资源的类都应该是不可复制的,或者具有专门的复制语义。反之亦然:任何不可复制或需要专门复制语义的类都在管理资源。在C++语言中,"管理资源"实际上意味着负责内存中的一些空间,或连接到网络或数据库,或处理文件,或撤消事务,等等
资源管理有很多例子。这些职责包括前缀操作、后缀操作,以及可能介于两者之间的一些操作。例如,内存管理包括获取一个内存地址的句柄,我们将对其进行管理,也许会打乱内存,最后释放句柄(因为如果你喜欢某个东西,就让它自由)。
template<typename T>
struct memory {
memory(T const& val = T()) : p(new T(val)) { }
~memory() { delete p }
T& operator*() const { return *p; }
private:
T* p;
};
// ...
{
memory<int> m0;
*m0 = 3;
std::cout << *m0 << 'n';
}
这个memory
类几乎是正确的:它自动获取底层内存空间并自动释放它,即使异常在获取其资源后传播了一段时间。但考虑一下这种情况:
{
memory<double> m1(3.14);
memory<double> m2(m1); // m2.p == m1.p (do you hear the bomb ticking?)
}
因为我们没有为memory
提供专门的复制语义,所以编译器提供了自己的复制构造函数和复制赋值。它们做了错误的事情:m2 = m1
意味着m2.p = m1.p
,这样两个指针指向同一地址。这是错误的,因为当m2
超出作用域时,它会像一个负责任的好对象一样释放资源,而当m1
超出作用域后,它也会释放资源,m2
已经释放了相同的资源,完成了双重删除——这是一种臭名昭著的未定义行为场景。此外,在C++中,复制对象非常容易,甚至不会注意到:一个函数按值获取参数,按值返回参数,或者按引用获取参数,然后调用另一个函数,该函数本身按值获取(或返回)参数。。。更容易假设的东西将尝试被复制。
所有这些都表明,当类的存在理由是管理资源时,您应该立即知道需要处理复制。你应该决定
- 您支持复制,而您决定复制意味着什么:安全共享资源,执行底层资源的深度复制,这样就不会有任何共享,或者将这两种方法结合起来,如写时复制或延迟复制。无论选择何种路径,都需要提供专门的复制构造函数和复制赋值运算符
- 或者您不支持对资源进行任何类型的复制,在这种情况下,您将禁用复制构造函数和复制赋值运算符
我想说的是,资源管理是禁用复制或提供专门复制语义的唯一情况。这只是《三条规则》的另一个视角。
这是一种非常常见的做法。有很多不适合复制的例子。
假设您的对象表示一个开放的服务器端套接字(即传入的网络连接);复制该对象的语义是什么?
当您只允许在检查后创建对象的实例时,就像在singleton的情况下一样,u需要私有构造函数。当构造函数被调用时,对象实例将被调用,然后检查是否已经有另一个实例是没有意义的。因此,我们所做的是从main调用类的成员函数,并在该成员函数内部检查内存中是否已经有另一个实例。如果不是,则调用构造函数。否则中止。检查singleton类或其他受保护的类,在这些类中,对象的数据必须保持安全,并且不应允许复制。
还可以检查一下:C++中的Singleton类
当你试图实现一个Singleton模式时,使用私有构造函数是完全可以接受的,因为它只在自己内部实例化,而不在其他地方实例化。一旦被调用,构造函数就不能被撤销。因此,只有在检查单例条件是否满足后,才会调用构造函数。
- if 子句中的赋值不起作用
- C++ 基本 CTOR 说明 - 为什么不调用赋值/复制构造函数
- 为什么我不能在返回 const 的布尔函数中为类成员变量赋值?C++
- 如何重载 const 对象的 [],以便值赋值不会导致编译错误
- 赋值不起作用,但带有取消引用运算符的地址起作用?
- 在声明上为类赋值不会编译
- 常量引用向量向量<向量<long>>赋值不编译
- 如果成员具有非平凡的noexcept赋值运算符,则默认的移动赋值不能显式为noexcept
- 类型 void 的值不允许在 C++ 中出错
- C++ int 赋值不起作用
- 不允许赋值和传递值
- 为什么函数头的更改会导致指针赋值不起作用
- 赋值给允许NULL成员的数组,而不使用堆
- 标准库中自赋值不安全的move赋值操作符的基本原理是什么?
- 如果允许将int赋值给float(反之亦然),是否不类型安全?
- 对指向std::list类型的共享指针赋值不起作用
- 为什么不允许使用赋值语法进行auto_ptr初始化
- 操作符重载,允许捕获右值,但不允许赋值
- 为什么函数中变量的最后一个赋值不能被视为移动?
- 为什么赋值不会使对象指向相同的位置