【问题标题】:Disable copy constructor禁用复制构造函数
【发布时间】:2011-08-29 22:43:57
【问题描述】:

我有一堂课:

class SymbolIndexer {
protected:
  SymbolIndexer ( ) { }

public:
  static inline SymbolIndexer & GetUniqueInstance ( ) 
  { 
    static SymbolIndexer uniqueinstance_ ;
    return uniqueinstance_ ; 
  }
};

我应该如何修改它以禁用如下代码:

SymbolIndexer symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

并且只允许这样的代码:

SymbolIndexer & ref_symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

【问题讨论】:

  • 顺便说一句,这是一个带有继承规定的单身人士(给予保护)?
  • 我对你的代码有疑问,每次创建不同的实例时,我认为 GetUniqueInstance() 总是会引用同一个对象。

标签: c++ copy-constructor


【解决方案1】:

学习引用与指针的 C++ 开发人员可能很想拥有

class MyClass {
    SomeClass acc;
public:
    MyClass() : acc() {}
    SomeClass &GetAccess( return acc; );
};

作为一种方便的方法:

someblock->{
    MyClass inst;
    auto acc = inst.GetAccess();  


    acc.SomeFunction();
}

这会在 SomeClass 内部产生双重释放问题,因为默认情况下您最终会得到一个副本...

【讨论】:

  • OP 知道这一点。他们想找到一种方法来阻止它。
  • 是的,我只是用更具体的细节来重申这个问题,说明为什么这是一个问题。上面的答案确实显示了如何隐藏副本或删除它。但是,如果有意义的话,您也可以在自己的复制构造函数中创建深层副本。这篇文章很危险地没有说明为什么这是一个更大的问题,而不仅仅是 C++ 语法是什么......
  • "我只是用更具体的细节来重申这个问题,说明为什么会出现这个问题。" 你没看懂我的意思。您将此作为“答案”发布。答案不是为了“重述问题”;他们是为了解决问题。这不是讨论和辩论的网络论坛。提出问题,提供答案。将问题重述为答案的部分可能是可以接受的,但如果你所做的只是重述它,那它就不是答案。
  • 问题是这个平台不允许你发表评论,直到你提供了足够的答案,这些答案已经被接受并得到批准才能实际发表评论。由于这个和其他关于这个平台的“你对我们不够好”的原因,我试图远离 StackOverflow。我正在尝试再次使用它,并且在不得不发布答案之后终于获得了评论的许可。
  • 您的文字也不适合发表评论;这只是对OP问题的重述,只是措辞不够好。此外,OP 已经得到了令人满意的答案,因此重新措辞他们的问题对任何人都没有帮助。此外,如果您不允许发表评论,这并不意味着您发表评论作为答案。评论几乎从来没有那么重要,你绝对必须找到一种方法来表达你想要表达的任何观点。继续前进,以其他方式发挥作用。
【解决方案2】:

您可以将复制构造函数设为私有而不提供任何实现:

private:
    SymbolIndexer(const SymbolIndexer&);

或者在 C++11 中,明确禁止:

SymbolIndexer(const SymbolIndexer&) = delete;

【讨论】:

  • 关于 delete 关键字我想添加以下内容。我目前在设计新类时的习惯是立即delete 复制构造函数和赋值运算符。我发现,根据上下文,它们大多是不必要的,删除它们可以防止一些意外行为的情况。如果出现可能需要复制 ctor 的情况,请确定是否可以使用移动语义来完成。如果这是不可取的,请为(!)复制 ctor 和赋值运算符提供一个实现。这是否是一个好方法我将留给读者。
  • @pauluss86 我喜欢你的方法,但我不会完全承诺,因为我认为遵循这种模式所花费的时间比它防止的错误所节省的时间要长。我只是在不确定时禁止复制。
  • @pauluss86 这基本上就是 Rust 所做的:默认移动(默认移动)。在我看来非常有帮助。
【解决方案3】:

如果你不介意多重继承(毕竟没那么糟糕),你可以用私有复制构造函数和赋值运算符编写简单的类,并附加子类:

class NonAssignable {
private:
    NonAssignable(NonAssignable const&);
    NonAssignable& operator=(NonAssignable const&);
public:
    NonAssignable() {}
};

class SymbolIndexer: public Indexer, public NonAssignable {
};

对于 GCC,这会给出以下错误消息:

test.h: In copy constructor ‘SymbolIndexer::SymbolIndexer(const SymbolIndexer&)’:
test.h: error: ‘NonAssignable::NonAssignable(const NonAssignable&)’ is private

不过,我不太确定这是否适用于每个编译器。有一个related question,但还没有答案。

UPD:

在 C++11 中,您还可以编写 NonAssignable 类,如下所示:

class NonAssignable {
public:
    NonAssignable(NonAssignable const&) = delete;
    NonAssignable& operator=(NonAssignable const&) = delete;
    NonAssignable() {}
};

delete 关键字防止成员被默认构造,因此它们不能在派生类的默认构造成员中进一步使用。尝试分配在 GCC 中会出现以下错误:

test.cpp: error: use of deleted function
          ‘SymbolIndexer& SymbolIndexer::operator=(const SymbolIndexer&)’
test.cpp: note: ‘SymbolIndexer& SymbolIndexer::operator=(const SymbolIndexer&)’
          is implicitly deleted because the default definition would
          be ill-formed:

UPD:

Boost 已经有一个用于相同目的的类,我猜它甚至以类似的方式实现。该类被称为boost::noncopyable,其用途如下:

#include <boost/core/noncopyable.hpp>

class SymbolIndexer: public Indexer, private boost::noncopyable {
};

如果您的项目政策允许,我建议您坚持使用 Boost 的解决方案。另请参阅另一个 boost::noncopyable-related question 了解更多信息。

【讨论】:

  • 不应该是NonAssignable(const NonAssignable &amp;other);吗?
  • 我认为这个问题如果更新为 C++11 delete 关键字语法会得到更多的支持。
  • @TomášZato:这个想法是保持复制构造函数和赋值运算符存在,但私有。如果你 delete 他们,它会停止工作(我刚刚检查过)。
  • @TomášZato:啊,抱歉,我的测试方法有点错误。删除也有效。将在一分钟内更新答案。
  • @Troyseph: const Class&amp;Class const&amp; 非常相似。对于指针,您甚至可能有 Class const * const 类型。
【解决方案4】:

SymbolIndexer( const SymbolIndexer&amp; ) 设为私有。如果您分配给参考,则不是复制。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    • 1970-01-01
    • 2013-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多