开发者

Prohibiting copy construction and copy assignment in C++

开发者 https://www.devze.com 2023-01-04 06:46 出处:网络
To prohibit copy construction and copy assignment, I\'ve seen the boost noncopyable class and in the Google style guide the DISA开发者_开发问答LLOW_COPY_AND_ASSIGN macro. Is there any reason to prefer

To prohibit copy construction and copy assignment, I've seen the boost noncopyable class and in the Google style guide the DISA开发者_开发问答LLOW_COPY_AND_ASSIGN macro. Is there any reason to prefer one of the techniques over the other, or any subtle differences one should be aware of?


I prefer boost noncopyable over the macro because it's not a macro and it is easier (IMO) to use.

In real code, i'm using neither of them and write the two declarations needed myself.

A subtle difference you might be interested in, though, is that using that macro or your own declarations will not give a compiler error when the class itself tries to copy the object (but rather fail to link later on). noncopyable however will also signal a compiler error in this case, because it's the base-class that has the functions declared private, not the class itself.


I always make the copy constructor and assignment operator private, without implementation:

  • this prevents someone making copies outside the class (will give compile errors)
  • this prevents the class itself also from making copies of itself (will give link errors)

I also add a clear comment (just above the prototypes of the copy constructor and assignment operator) to indicate that I do this to prevent someone making copies (as a reminder to future developers).

In my opinion, this solution is clear, does not rely on external libraries or macro's and is easy to understand.


Based on the rule of avoid inheritance wherever possible, I always use a macro - in this case my own. Of course, there is the other rule, about avoiding macros...


I would never use the DISALLOW_COPY_AND_ASSIGN macro because you have to put it in the private part of the class declaration. What if you (or someone maintaining your code) by mistake put it in the public part? The problem is that it is not obvious at all that "DISALLOW_COPY_AND_ASSIGN" should be put in the private part: the name of the macro seems to imply that it will disallow copy and assignment independently of where you have declared it.

The noncopyable base class avoids this issue entirely.

However, the fastest, most readable, and portable way is to explicitly declare the constructors in the private part of the class.


The two techniques are [practically] the same. One uses a macro to save typing (you still have to place it where you would normally place the code) and the other provides the facility through a base class that you extend.

Avoiding macros is a good thing but extending a class just to avoid placing the copy constructor in the private section of your class seems confusing as a reader.

Any reason you dont just do what these two methods do within your class?


Consider the following code (heavily inspired by noncopyable_test.cpp on boost's website):

#include <boost/noncopyable.hpp>
#include <iostream>
using namespace std;

#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&);   \
  void operator=(const TypeName&)

class BoostNoCopy : boost::noncopyable {
 public:
  BoostNoCopy() { cout << "BoostNoCopy failed." << endl; }
};

class MacroNoCopy {
 public:
  MacroNoCopy() { cout << "MacroNoCopy failed." << endl; }
 private:
  DISALLOW_COPY_AND_ASSIGN(MacroNoCopy);
};

class ExplicitNoCopy {
 public:
  ExplicitNoCopy() { cout << "ExplicitNoCopy failed." << endl; }
 private:
  ExplicitNoCopy(const ExplicitNoCopy&);
  void operator=(const ExplicitNoCopy&);
};

int main() {
  typedef BoostNoCopy NotCopyable;

  NotCopyable a;
  NotCopyable b(a);
  a = b;

  return 0;
}

By setting the typedef in main() to BoostNoCopy, MacroNoCopy, or ExplicitNoCopy, you get the following compiler errors (using i686-apple-darwin11-llvm-g++-4.2):

boost::noncopyable

/usr/local/include/boost/noncopyable.hpp: In copy constructor ‘BoostNoCopy::BoostNoCopy(const BoostNoCopy&)’:
/usr/local/include/boost/noncopyable.hpp:27: error: ‘boost::noncopyable_::noncopyable::noncopyable(const boost::noncopyable_::noncopyable&)’ is private
main.cpp:9: error: within this context
main.cpp: In function ‘int main()’:
main.cpp:33: note: synthesized method ‘BoostNoCopy::BoostNoCopy(const BoostNoCopy&)’ first required here 
/usr/local/include/boost/noncopyable.hpp: In member function ‘BoostNoCopy& BoostNoCopy::operator=(const BoostNoCopy&)’:
/usr/local/include/boost/noncopyable.hpp:28: error: ‘const boost::noncopyable_::noncopyable& boost::noncopyable_::noncopyable::operator=(const boost::noncopyable_::noncopyable&)’ is private
main.cpp:9: error: within this context
main.cpp: In function ‘int main()’:
main.cpp:34: note: synthesized method ‘BoostNoCopy& BoostNoCopy::operator=(const BoostNoCopy&)’ first required here 

Macro

main.cpp: In function ‘int main()’:
main.cpp:18: error: ‘MacroNoCopy::MacroNoCopy(const MacroNoCopy&)’ is private
main.cpp:33: error: within this context
main.cpp:18: error: ‘void MacroNoCopy::operator=(const MacroNoCopy&)’ is private
main.cpp:34: error: within this context

Explicit

main.cpp: In function ‘int main()’:
main.cpp:25: error: ‘ExplicitNoCopy::ExplicitNoCopy(const ExplicitNoCopy&)’ is private
main.cpp:33: error: within this context
main.cpp:26: error: ‘void ExplicitNoCopy::operator=(const ExplicitNoCopy&)’ is private
main.cpp:34: error: within this context

Even if you can parse through the compiler error given when using boost::noncopyable, it definitely takes much longer to do so. For the macro and explicit cases, the error is pretty obvious.

So, it seems that the tradeoff is that the boost::noncopyable use requires knowledge of what it means, how it's used, and how to parse the compiler errors; but, it's much more succinct in the code. The macro (Google/Qt technique) requires the macro definition (probably in some other header), and knowledge of how to use it; however, the compiler errors are very readable (identical to the explicit). Lastly, the explicit technique is most obvious, but may require extra typing by the programmer.

0

精彩评论

暂无评论...
验证码 换一张
取 消