77

为了防止复制一个类,你可以很容易地声明一个私有的复制构造函数/赋值操作符。但是你也可以继承boost::noncopyable

在这种情况下使用 boost 有什么优点/缺点?

4

11 回答 11

56

我没有看到任何文档优势:

#include <boost/noncopyable.hpp>

struct A
    : private boost::noncopyable
{
};

与:

struct A
{
     A(const A&) = delete;
     A& operator=(const A&) = delete;
};

当您添加仅移动类型时,我什至认为文档具有误导性。以下两个示例不可复制,尽管它们是可移动的:

#include <boost/noncopyable.hpp>

struct A
    : private boost::noncopyable
{
    A(A&&) = default;
    A& operator=(A&&) = default;
};

与:

struct A
{
    A(A&&) = default;
    A& operator=(A&&) = default;
};

在多重继承下,甚至会出现空间惩罚:

#include <boost/noncopyable.hpp>

struct A
    : private boost::noncopyable
{
};

struct B
    : public A
{
    B();
    B(const B&);
    B& operator=(const B&);
};

struct C
    : public A
{
};

struct D
    : public B,
      public C,
      private boost::noncopyable
{
};

#include <iostream>

int main()
{
    std::cout << sizeof(D) << '\n';
}

对我来说,这打印出来:

3

但是这个,我相信它有更好的文档:

struct A
{
    A(const A&) = delete;
    A& operator=(const A&) = delete;
};

struct B
    : public A
{
    B();
    B(const B&);
    B& operator=(const B&);
};

struct C
    : public A
{
    C(const C&) = delete;
    C& operator=(const C&) = delete;
};

struct D
    : public B,
      public C
{
    D(const D&) = delete;
    D& operator=(const D&) = delete;
};

#include <iostream>

int main()
{
    std::cout << sizeof(D) << '\n';
}

输出:

2

我发现声明我的复制操作比推理我是否来自boost::non_copyable多次以及这是否会花费我要容易得多。特别是如果我不是完整继承层次结构的作者。

于 2011-10-20T19:42:44.317 回答
46

总结一下别人说的:

boost::noncopyable私有复制方法的优点

  1. 它的意图更加明确和描述性。使用私有复制函数是一个比noncopyable.
  2. 它是更少的代码/更少的输入/更少的混乱/更少的错误空间(最简单的可能是意外提供一个实现)。
  3. 它将含义嵌入到类型的元数据中,类似于 C# 属性。您现在可以编写一个只接受不可复制对象的函数。
  4. 它可能会在构建过程的早期捕获错误。如果类本身或类的朋友正在执行错误的复制,错误将在编译时而不是链接时出现。
  5. (几乎与#4 相同)防止类本身或类的朋友调用私有复制方法。

私有复制方法的优点boost::noncopyable

  1. 没有升压依赖
于 2011-10-20T16:29:18.413 回答
44

它使意图明确和明确,否则必须查看类的定义,并搜索与复制语义相关的声明,然后查找声明它的访问说明符,以确定是否类是否不可复制。通过编写需要启用复制语义的代码并查看编译错误来发现它的其他方法。

于 2011-10-19T15:43:21.010 回答
17
  1. boost::noncopyable 的意图更加清晰。
  2. Boost::noncopyable 防止类方法意外使用私有复制构造函数。
  3. 使用 boost::noncopyable 的代码更少。
于 2011-10-19T15:44:52.483 回答
16

我不明白为什么似乎没有其他人提到它,但是:

noncopyable你一起写一次你的班级名称。

没有,五重重复:一个 A 代表“A 类”,两个禁用赋值,两个禁用复制构造函数。

于 2011-10-30T09:29:58.830 回答
10

引用文档:

“处理这些问题的传统方法是声明一个私有复制构造函数和复制赋值,然后记录这样做的原因。但是从不可复制派生更简单、更清晰,并且不需要额外的文档。”

http://www.boost.org/libs/utility/utility.htm#Class_noncopyable

于 2011-10-19T15:45:10.893 回答
8

一个具体的优势(除了更清楚地表达您的意图之外)是,如果成员或友元函数试图复制对象,则在编译阶段而不是链接阶段会更快地发现错误。基类构造函数/赋值在任何地方都无法访问,从而产生编译错误。

它还可以防止您意外定义函数(即键入{}而不是;),这是一个很可能被忽视的小错误,但随后会允许成员和朋友制作对象的无效副本。

于 2011-10-19T15:45:21.250 回答
4

一个缺点(特定于 GCC)是,如果你编译你的程序g++ -Weffc++并且你有包含指针的类,例如

class C : boost::noncopyable
{
public:
  C() : p(nullptr) {}

private:
  int *p;
};

GCC 不明白发生了什么:

警告:“C 类”具有指针数据成员 [-Weffc++]
警告:但不覆盖“C(const S&)”[-Weffc++]
警告:或“运算符=(const C&)”[-Weffc++]

虽然它不会抱怨:

#define DISALLOW_COPY_AND_ASSIGN(Class) \
  Class(const Class &) = delete;     \
  Class &operator=(const Class &) = delete

class C
{
public:
  C() : p(nullptr) {}
  DISALLOW_COPY_AND_ASSIGN(C);

private:
  int *p;
};

PS 我知道 GCC 的 -Weffc++ 有几个问题。无论如何,检查“问题”的代码非常简单......有时它会有所帮助。

于 2014-03-05T12:09:22.983 回答
3

优点是您不必自己编写私有复制构造函数和私有复制运算符,它清楚地表达了您的意图,而无需编写额外的文档。

于 2011-10-19T15:43:38.480 回答
2

我宁愿使用 boost::noncopyable 而不是手动删除或私有化复制构造函数和赋值运算符。

但是,我几乎从不使用任何一种方法,因为:

如果我正在制作一个不可复制的对象,那么它是不可复制的肯定是有原因的。这个原因,99% 的时间,是因为我的成员无法有意义地复制。很有可能,这些成员也更适合作为私有实现细节。所以我做大多数这样的类:

struct Whatever {
  Whatever();
  ~Whatever();
  private:
  struct Detail;
  std::unique_ptr<Detail> detail;
};

所以现在,我有一个私有实现结构,并且由于我使用了 std::unique_ptr,我的顶级类是不可免费复制的。来自此的链接错误是可以理解的,因为它们谈论您如何无法复制 std::unique_ptr。对我来说,这就是 boost::noncopyable 和私有实现合二为一的所有好处。

这种模式的好处是稍后,如果我决定我确实想让我的这个类的对象可复制,我可以添加和实现一个复制构造函数和/或赋值运算符而不改变类层次结构。

于 2011-10-20T19:03:22.517 回答
1

缺点,根据斯科特迈耶斯的说法,这个名字是“非自然的”,如果你确实需要找到它的缺点。

于 2011-10-19T15:46:57.600 回答