C++ 类,用于防止对象复制

2024年8月28日 | 阅读 4 分钟

C++ 类实例有时根本不应该被克隆。阻止此类对象复制有三种方法:不可复制的混入、私有拷贝构造函数和赋值运算符,或者删除这些特定的成员函数。

移动表示文件包装器流的类实例是不合适的。它会使实际的 I/O 系统处理复杂化。类似地,如果实例具有特殊的私有对象,则复制指针是无用的。对象切片问题是一个有些相似但并非完全相同的话题。

下图所示的简单类 Vehicle 旨在拥有一个唯一的拥有者,即 Person 的一个实例。

因此,实现 Person 只需执行以下操作

一个名为 info() 的辅助方法被实现以说明问题,如下所示

这个例子清楚地表明 Car 实例不能被克隆。例如,同一个所有者不应该自动拥有相同汽车的第二个副本。事实上,当执行以下代码时

将给出输出

Owner is Joe Sixpack
Owner is Joe Sixpack

如何阻止这种意外的对象复制?

方法 1:私有拷贝构造函数和拷贝赋值运算符

声明拷贝赋值运算符和拷贝构造函数为私有是一种常用策略。甚至不需要它们的实现。目的是确保任何尝试完成复制或赋值的操作都会导致编译错误。

在上面的例子中,Car 将被修改为如下所示。请仔细检查私有成员中的另外两个类成员。

现在,如果我们再次尝试将 Car 实例赋值给一个新实例,编译器将大声抗议

如果编写两行包含相同名称的代码太耗时,可以使用宏代替。根据 WebKit 的 wtf/Noncopyable.h 中的 WTF MAKE NONCOPYABLE 宏,WebKit 采用了这种方法(不用担心,在 WebKit 源代码的上下文中,这里的 WTF 代表 Web Template Framework)。Chrome 浏览器代码中使用的 base/macros.h 文件中的 DISALLOW COPY 和 DISALLOW ASSIGN 宏区分了拷贝构造函数和赋值。

方法 2:不可复制的混入

上述概念可以扩展,以构建一个特定类,其主要功能是禁止对象复制。它通常被称为 Noncopyable,并经常用作混入。因此,我们示例中的 Car 类可以继承自此 Noncopyable。

Boost 用户可能已经熟悉上述混入的 Boost 版本,boost::noncopyable。以下是该混入的概念性独立实现示例

我们出色的 Car 类的写法如下

与第一个选项相比,使用 Noncopyable 的优点在于目标非常明确。只需快速查看类的第一行,您就可以判断其实例不应该被克隆。

方法 3:删除了拷贝赋值运算符和拷贝构造函数

对于较新的应用程序,上述变通方法变得越来越不必要。C++11 突然使问题变得简单:只需删除拷贝构造函数和赋值运算符。相反,我们的类将如下所示

值得注意的是,如果您将 boost::noncopyable 混入与支持 C++11 的编译器一起使用,boost::noncopyable 实现也会自动删除上述成员函数。

使用此方法,任何意外的复制都会产生更友好的错误消息