11

From what I understand I'm able to "disable" copying and assigning to my objects by defining private copy constructor and assignment operator:

class MyClass
{
private:
    MyClass(const MyClass& srcMyClass);
    MyClass& operator=(const MyClass& srcMyClass);
}

But what's the usage of this?
Is it considered a bad practice?

I would appreciate if you could describe the situation, in which it would be reasonable / useful to "disable" assignment and copy constructor in this way.

LihO
  • 37,789
  • 9
  • 89
  • 156

5 Answers5

11

It's useful when it doesn't make sense for your object to be copied. It is definitely not considered bad practice.

For instance, if you have a class that represents a network connection, it's not meaningful to copy that object. Another time you may want a class to be noncopyable is if you had a class representing one player in a multiplayer game. Both these classes represent things that can't be copied in the real world, or that don't make sense to copy (a person, a connection).

Also, if you are trying to implement a Singleton, it's standard procedure to make the objects non-copyable.

Seth Carnegie
  • 70,115
  • 19
  • 169
  • 239
  • Thanks, it makes sense. What about assignment operator? – LihO Jan 29 '12 at 20:23
  • 3
    @LihO generally if you disable one you disable the other. If you don't disable the assignment operator, you can do this: `MyClass a, b; a = b;` and if you don't disable the copy constructor you can do `MyClass b; MyClass a(b);` So if you don't disable both, you can get around the other one being disabled. – Seth Carnegie Jan 29 '12 at 20:25
5

Generally speaking any class that manages a resource should be none-copyable or have specialized copy semantics. The converse is true as well: any class that is non-copyable or needs specialized copy semantics is managing a resource. "Manage a resource" in the C++ lingua in practice means responsible for some space in memory, or for a connection to a network or a database, or a handle to a file, or an undo transaction, and so on.

Resource management captures quite a lot of examples. These are responsibilities that take a prefix operation, a suffix operation and possibly some action in between. Memory management, for example, involves acquiring a handle to a memory address which we'll manage, perhaps mess around with that memory, and finally release the handle (because if you love something, let it be free).

template<typename T>
struct memory {
    memory(T const& val = T()) : p(new T(val)) { } 
    ~memory() { delete p }
    T& operator*() const { return *p; }
private:
    T* p;
};

// ...
{
    memory<int> m0;
    *m0 = 3;
    std::cout << *m0 << '\n';
}

This memory class is almost correct: it automatically acquires the underlying memory space and automatically releases it, even if an exception propagates some time after it acquired its resource. But consider this scenario:

{
    memory<double> m1(3.14);
    memory<double> m2(m1);  // m2.p == m1.p (do you hear the bomb ticking?)
}

Because we didn't provide specialized copy semantics for memory, the compiler provides its own copy constructor and copy assignment. These do the wrong thing: m2 = m1 means m2.p = m1.p, such that the two pointers point at the same address. It's wrong because when m2 goes out of scope it frees its resource like a good responsible object, and when m1 then goes out of scope it too frees its resource, that same resource m2 has already freed, completing a double-delete -- a notorious undefined behaviour scenario. Moreover, in C++ it's extremely easy to make copies of an object without even noticing: a function taking its parameter by value, returning its parameter by value, or taking its parameter by reference but then calling another function which itself takes (or returns) its parameter by value ... It's easier to just assume that things will try to get copied.

All this to say that when a class' raison d'être is managing a resource then you immediately should know that you need to handle copying. You should decide

  • you support copying, whereas you decide what copying means: safe sharing of the resource, performing a deep copy of the underlying resource so there is no sharing whatsoever, or combining the two approaches as in copy-on-write or lazy copy. Whatever path you choose you will need to provide a specialized copy constructor and a copy assignment operator.
  • or you don't support any sort of copying of the resource, in which case you disable the copy constructor and the copy assignment operator.

I'd go so far and say that resource management is the only case where you disable copying or provide specialized copy semantics. This is just another perspective on The Rule of Three.

wilhelmtell
  • 53,297
  • 19
  • 89
  • 128
2

It's a pretty common practice. There are are lots of examples where copying isn't appropriate.

Let's say your object represents an open server-side socket (i.e. an incoming network connection); what would be the semantics of making a copy of that object?

NPE
  • 438,426
  • 93
  • 887
  • 970
0

When you are trying to implement a singleton pattern it's perfectly acceptable to use a private constructor as it's meant to be only instantiated inside itself and from nowhere else. Once invoked, the constructor can't be revoked. So the constructor is invoked only after checking if the singleton condition satisfied.

Harley Holcombe
  • 155,163
  • 15
  • 67
  • 62
TigOldBitties
  • 1,249
  • 8
  • 14
0

when you are allowed to create instance of object only after checking like in the case of singleton u need private constructors. when constructor is called the object instance will be called and then there is no point in checking if there is another instance already. so what we do is call a member function of class from main and inside that member function check if another instance is already in the memory. if not constructor is called. else aborted. check singleton classes or other protected classes where data of object has to be kept secured and should not be allowed to copy.

also check this : Singleton Class in C++

Community
  • 1
  • 1
Rohit Vipin Mathews
  • 10,853
  • 15
  • 49
  • 100