C++ 防止对象复制的类

C++ 防止对象复制的类

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

将代表文件封装流的类的实例移动是不合适的。这将使实际的I/O系统处理变得复杂。类的实例拷贝指针是无用的,如果实例有特殊的私有对象。对象切割的问题是一种相对可比较的问题,但不完全是同一个主题。

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

class Car {
public:
  Car(): owner() {}

  void setOwner(Person *o) { owner = o; }
  Person *getOwner() const { return owner; }
  void info() const;
private:
  Person *owner;
};

实施人员之所以只需要做以下工作:

struct Person {
  std::string name;
};

一个名为info()的辅助方法被实现用于说明问题,示例如下:

void Car::info() const
{
  if (owner) {

    std::cout < < "Owner is " << owner->name < < std::endl;
  } else {
    std::cout << "This car has no owner." << std::endl;
}

这个示例清楚地表明Car实例不能被克隆。例如,同一个车主不应该自动拥有一个相同车辆的副本。实际上,当执行下面的代码时:

Person joe;
  joe.name = "Joe Sixpack";


  Car sedan;
  sedan.setOwner(&joe);
  sedan.info();
  Car anotherSedan = sedan;
  anotherSedan.info();

将会输出:

Owner is Joe Sixpack
Owner is Joe Sixpack

如何停止这个无意拷贝的对象?

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

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

汽车将被修改为在上面的示例中的样子。检查另外两个私有成员类。

class Car {
public:
  Car(): owner() {}

  void setOwner(Person *o) { owner = o; }
  Person *getOwner() const { return owner; }
  void info() const;
private:
  Car(const Car&);
  Car& operator=(const Car&);
  Person *owner;
};

现在,如果我们尝试再次将Car的实例分配给一个新实例,编译器会大声抗议:

example.cpp:35:22: error: calling a private constructor of class 'Car'
  Car anotherSedan = sedan;
                     ^

example.cpp:22:3: note: declared private here
  Car(const Car&);
  ^
1 error generated.

如果写下相同的名称会耗费太多时间,可以使用宏代替。WebKit在wtf/Noncopyable.h中使用了 WTF MAKE NONCOPYABLE 宏来实现这一点(请不要惊慌,在 WebKit 源代码的上下文中,这里的 WTF 代表 Web 模板框架)。Chrome 浏览器的代码中使用了 base/macros.h 文件中的 DISALLOW COPY 和 DISALLOW ASSIGN 宏,用于区分拷贝构造函数和赋值操作。

方法2:不可拷贝的 mixin

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

使用 Boost 的用户可能已经熟悉了 Boost 版本的上述 mixin,即 boost::noncopyable。以下是一个概念上独立的 mixin 实现的示例:

class NonCopyable
{
  protected:

    NonCopyable() {}
    ~NonCopyable() {}
  private: 
    NonCopyable(const NonCopyable &);
    NonCopyable& operator=(const NonCopyable &);
};

我们美妙的Car类的拼写如下:

Class Car: private NonCopyable {
public:
  Car(): owner() {}

  void setOwner(Person *o) { owner = o; }
  Person *getOwner() const { return owner; }
  }
private:
  Person *owner;
};

使用Noncopyable的好处是相对于第一种选择,目标非常明显。只要看一眼类的初始行,就可以知道它的实例不应该被克隆。

方法3:删除复制赋值操作符和复制构造函数

对于较新的应用程序,上述的变通方法越来越不必要。C++11突然变得很简单:只需摒弃复制构造函数和赋值操作符。我们的类将如下所示:

class Car {
public:
  Car(const Car&) = delete;

  void operator=(const Car&) = delete;
  Car(): owner() {}
  void setOwner(Person *o) { owner = o; }
  Person *getOwner() const { return owner; }
private:
  Person *owner;
};

需要注意的是,如果你使用支持C++11的编译器将boost::noncopyable mixin与类一同使用,boost::noncopyable的实现也会自动去除上述的成员函数。

任何意外的拷贝行为都会产生更友好的错误信息:

example.cpp:34:7: error: call to deleted constructor of 'Car'
  Car anotherSedan = sedan;
      ^              ~~~~~

example.cpp:10:3: note: 'Car' has been explicitly marked deleted here
  Car(const Car&) = delete;
  ^

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程