How do I make this C++ object non-copyable?
Asked Answered
O

10

80

See title.

I have:

class Foo {
   private:
     Foo();
   public:
     static Foo* create();
}

What need I do from here to make Foo un-copyable?

Other answered 31/1, 2010 at 22:59 Comment(0)
A
109
class Foo {
   private:
     Foo();
     Foo( const Foo& ); // non construction-copyable
     Foo& operator=( const Foo& ); // non copyable
   public:
     static Foo* create();
}

If you're using boost, you can also inherit from noncopyable : http://www.boost.org/doc/libs/1_41_0/boost/noncopyable.hpp

EDIT: C++11 version if you have a compiler supporting this feature:

class Foo {
   private:
     Foo();
   public:
     Foo( const Foo& ) = delete; // non construction-copyable
     Foo& operator=( const Foo& ) = delete; // non copyable

     static Foo* create();
}

Note that deleted methods should be public: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-delete

Azerbaijani answered 31/1, 2010 at 23:2 Comment(6)
As a matter of interest, why have you made the default constructor private, and added a create() method? What advantages does this layout have?Motorboat
@EdwardBird I was just using the question example. This way of doing is basically like forcing construction of a particular type's instances through a factory. This is useful if the constructor should do basic setup and some other operations (maybe different depending on the context or platform or whatever) have to be done before providing the object, or even before creating the object (maybe some memory pool manipulation). I would have used a unique_ptr or shared_ptr as create() return type personally. Anyway the main reason was just fixing the question example.Azerbaijani
Disabling copy construction and copy assignment operator disables move construction and assignment as well. Move operations will still function by falling back to copying. Re-enable them by explicitly setting them to 'default'. Something to be aware of.Candler
@Candler - important catch, but how move will fallback to copy if copy is already deleted?Toxicosis
If there is no default constructor, can I keep my constructor public?Discussant
It is a good practice to put the deleted methods into the public section.Rangefinder
S
30

Make the copy constructor and the assignment operator private as well. Just the declaration is enough, you don't have to provide an implementation.

Sthenic answered 31/1, 2010 at 23:0 Comment(0)
O
22

In C++11, you can explicitly disable the creation of default copy and assignment constructor by placing = delete after the declaration.

From Wikipedia:

struct NonCopyable {
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable & operator=(const NonCopyable&) = delete;
};

The same goes for classes of course.

Orsa answered 29/11, 2012 at 15:16 Comment(0)
V
20

Just another way to disallow copy constructor,For convenience, a DISALLOW_COPY_AND_ASSIGN macro can be used:

// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&) = delete;      \
  void operator=(const TypeName&) = delete

Then, in class Foo:

class Foo {
 public:
  Foo(int f);
  ~Foo();

 private:
  DISALLOW_COPY_AND_ASSIGN(Foo);
};

ref from google style sheet

Victualage answered 1/2, 2010 at 8:31 Comment(11)
Your solution doesn't work as-is with some compilers. Some C++ compilers require that if you declare a class member function then you must also define it, even if it's never used in code. So you need to use {} with the two function declarations above.Overglaze
@ThreeBit, If you mean constructor with one param and destructor by saying "two function", these are decleration and programmer already knows that these will have definition in somewhere else. Other than that, it is same as accepted answer.Butyrate
@ThreeBit: Which compilers do you mean? If they do so, they don't comply to the standard.Rosenfeld
@Sebastian that's not true. If a class constructor is declared then it must somewhere be defined. The fact that some popular compilers allow you to omit the definition of a class member function if the function is not explicitly called is a compiler extension to the language. The Green Hills C++ compiler is an example of one that is strict in this regard. I can find no location in the C++ Standard that requires compilers to skip the linkage of a member function if it's not used. Feel free to find such a clause. Perhaps you can find one.Overglaze
@ThreeBit: Interesting discussion. What I found in the standard: "There shall be at most one definition of a non-inline member function in a program; no diagnostic is required.". Then, for local classes: "Member functions of a local class shall be defined inline in their class definition, if they are defined at all.". I don't find anything that forbids member function declarations that have no corresponding definition.Rosenfeld
@Sebastian well I can't see how that first statement could be explicitly allowing non-definition, as zero is valid for the case of there being an inline definition available. That second statement seems a little stronger, but I think it's fair to want something more concrete. I don't think that your inability to find something that forbids it means that it's allowed. The standard can't be interpreted that way, as it would allow nonsensical things as a result.Overglaze
@Sebastian That being said, in section 3 of the C++ Standard is the One Definition Rule, and includes: "Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program". While that doesn't say anything about an unused function, I think that the Green Hills compiler people could have argued that the function was indeed used, just not directly.Overglaze
@ThreeBit: I am not sure about that one. Would the function be virtual, than upon derivation there might be usage. But for non-virtual functions, it's a "hmm".Rosenfeld
@Overglaze No, odr-use is odr-use. If you don't odr-use the function, it doesn't need to be defined. The Green Hills compiler is non-compliant.Alister
reference link is brokenTalesman
I like the idea of the macro, but I believ it should stay in the public section of the class. As stated in other answers: the deleted methods should be publicFraya
E
19
#include <boost/utility.hpp>
class Foo : boost::noncopyable {...

But as Scott Meyers once said..."It's a fine class, it's just that I find the name a bit un, err non natural", or something like that.

Elishaelision answered 31/1, 2010 at 23:4 Comment(2)
Any link to the context of the quote?Granicus
Reference: Effective C++ (Third Edition) - Scott Meyers, Item 6Guadiana
S
17

To add a bit there.

The traditional solution is, as has been said, to declare both Copy Constructor and Assignment Operator as private, and not to define them.

  • Because they are private, this will lead to a compile-time error from anyone trying to use them that has not access to the private parts of the class...
  • Which leaves friends (and the class itself) for which the error will occur under the form of undefined symbol, either at link-time (if you check for those there) or most probably at run-time (when trying to load the library).

Of course, it is quite a bother in the second case because you then have to check your code by yourself since you do not have the indication of the file and line at which the error occurs. Fortunately it's limited to your class methods and friends.


Also, it is worth noting that these properties are transitive down the inheritance and composition road: the compiler will only generate default versions of the Default Constructor, the Copy Constructor, the Assignment Operator and the Destructor if it may.

This means that for any of those four, they are automatically generated only if they are accessible for all the bases and attributes of the class.

// What does boost::noncopyable looks like >
class Uncopyable {
public:
  Uncopyable() {}

private:
  Uncopyable(const Uncopyable&);
  Uncopyable& operator=(const Uncopyable&);
};

This is why inheriting from this class (or using it as an attribute) will effectively prevents your own class to be copyable or assignable unless you define those operators yourself.

Generally inheritance is chosen over composition there for 2 reasons:

  • The object is effectively Uncopyable, even if polymorphism may not be that useful
  • Inheritance leads to EBO or Empty Base Optimization, while an attribute will be addressable and thus will occupy memory (in each instance of the class) even if it does not actually need it, the compiler has the possibility not to add this overhead for a base class.

You could, alternatively, declare the operators private and not define them in your own class, but the code would be less self-documenting, and you would not be able to automatically search for those class that have this property then (unless you have a full-blown parser).

Hope this shed some light on the mechanism.

Schizogenesis answered 1/2, 2010 at 8:17 Comment(9)
BTW, isn't Uncopyable incomplete without explicitly defining the constructor since it won't be automatically generated on account of the presence of the other constructors? For example, you get "no appropriate default constructor available" with this: rextester.com/SFWR22041 Thanks for your helpful answer! I especially appreciate the motivation you've given for using inheritance.Mcclees
@chappjc: And you are right again, I really should have compiled the code.Schizogenesis
@MatthieuM. I disagree with the statement "This is why inheriting from this class (or using it as an attribute) will effectively prevents your own class to be copyable or assignable unless you define those operators yourself." Because even defining the copy constructor and assignment operator explicitly will still enforce the non-copyable feature due to inheritance hierarchy.Lathery
@johnsmith: I am afraid you are confused about the rules. I can assure you it works perfectly and just advise you to try it out (for example on ideone or Coliru).Schizogenesis
@MatthieuM. Sorry to burst you bubble, check to try for yourself. After which kindly edit your answer to state the correct fact. The compiler will not allow you to define your own copy constructor and use it while you are inheriting from a non-copyable classLathery
@johnsmith: and yet, you can make it work :)Schizogenesis
@MatthieuM. turns out you are correct after all, I learnt something :)Lathery
@MatthieuM. - Also my misconception lied in the fact that when a derived object is created via copy construction, the base class default copy constructor is invoked due to which the process will fail, but however its default constructor is invoked and hence this mechanism is possibleLathery
@johnsmith: glad I could teach you something :) and yes, indeed, you have to really take the reins here. It's actually very similar to having a "const" attribute.Schizogenesis
M
7

The typical way to make a C++ object non-copyable is to explicitly declare a copy constructor and copy-assignment operator but not implement them. This will prevent the compiler from generating its own. (Typically this is done in conjunction with declaring them private so that it generates a compilation error instead of a linker error.)

There also is the boost::noncopyable class that you can inherit from, which does what I described above.

Maxey answered 31/1, 2010 at 23:2 Comment(0)
P
5

The good practice in C++11 is to declare the copy constructor and assignment as publicly deleted. Not privately deleted, publicly deleted: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-delete

Podium answered 21/8, 2018 at 15:35 Comment(0)
S
3

Make the copy constructor private.

Foo(const Foo& src);

You don't need to implement it, just declare it in the header file.

Sample answered 31/1, 2010 at 23:0 Comment(0)
P
2

This is what I use:

/* Utility classes */

struct NoCopy
{
public:
    NoCopy() {}
private:
    NoCopy(const NoCopy &);
};

struct NoAssign
{
private:
    NoAssign &operator=(const NoAssign &);
};

struct NonInstantiable
{
private:
    NonInstantiable();
};

struct NoCopyAssign : NoCopy, NoAssign
{
};
typedef NoCopyAssign NoAssignCopy;

In your case:

struct Example : NoCopy
{
};
Popp answered 1/2, 2010 at 8:24 Comment(1)
Note that inheriting from utility classes like this can adversely affect class size, depending on architecture ABI. See trac.webkit.org/changeset/68414 for details. Granted, that changeset only mentions Itanic, and nothing else -- but is it worth relying on no other architectures ever doing this? Maybe. It's a definite risk, and declaring a private constructor and assignment operator works equally well.Flat

© 2022 - 2024 — McMap. All rights reserved.