Am I violating Rule of three?

944 views Asked by At

I recently read, Rule of three and am wondering if I am violating it?

In my GUI application, classes like MainFrame, Interface, Circuit, Breadboard etc. (class name are indicative) have a single instance of each of them. In their constructors, I have allocated some resources (memory), which I safely release in their destructors.

So I have defined only destructor, but not copy constructor and assignment operator.

I am sure I don't need them, but I am curious if I am violating the rule, and what can/should I do to follow it?

5

There are 5 answers

3
R. Martinho Fernandes On BEST ANSWER

The rule of three is about dealing with all the Big Three, but that does not necessarily mean you'll have to define them if you don't want to. Either you provide them or you forbid them. What you shouldn't do is ignore them.


So I have defined only destructor, but not copy constructor and copy operator.
Am I violating Rule of three?

Yes, you are in violation of the rule. The compiler will generate a copy constructor and copy assignment operator, and since you allocate memory in the constructor and release in the destructor, these copies will have wrong semantics: they'll copy the pointers, and you will have two classes aliasing the same memory. The assignment won't even release the old memory, and simply overwrite the pointer.

Is this a problem?

If, like you imply, you don't make copies or assign to instances of those classes, nothing will go wrong. However, it's better to be on the safe side and declare (and don't even bother defining) the copy constructor and copy assignment operator private, so you don't invoke them accidentally.

In C++11 you can use the = delete syntax instead:

T(T const&) = delete; // no copy constructor
T& operator=(T const&) = delete; // no copy assignment
2
adontz On

You should declare (but not implement) a private copy constructor and assignment operator. Make sure you don't implement the functions. This will prevent any kind of copying of classes not supposed to be copied.

0
Sion Sheevok On

Yes, it does violate the rule of three as per that definition.

It is, however, a "rule of thumb". A general guideline. If you don't need copy construction or assignment operations, don't implement them. Others have suggested declaring them as private and defining them as empty. I'd go one step further and say don't even define them.

If you define them, then you could potentially still invoke the empty methods. Instead, leave them undefined and, if you ever try to invoke those methods, you will receive a linker error because the method definitions could not be found. Favor build-time errors over run-time errors/undesired behavior.

0
Arunmu On

It depends a lot on you application logic and how the you have documented your interface classes to the users.

Normally, a good c++ programmer must be aware of rule of three (and a half if you know the "copy and swap idiom") and 5 and a 1/2 in case of c++11 (Move semantics).

If you class manages resource and if the same class is copyable (i.e copy ctor and assigment operator not defined as private) then its very important to do deep copying by writing your own copy ctor and assignment operator.

But If you are always toying your class by passing them as REFERENCE then better define a default copy ctor and assignment operator as private so that even if you pass by valy or copy by mistake, the compiler would warn you.

0
Paul Manta On

If you don't need it, don't follow it. The motivation behind the rule of three is that, when you need a destructor, that is usually because you need to do some dynamic deallocations.

If you do deallocations as well, you're going to need the copy constructor and assignment operators as well. Imagine you have a class that has a pointer to something:

struct Foo
{
    Foo() { ptr_ = new int; }
    ~Foo() { delete ptr_; }
    int* ptr_;
};

Because you don't define a copy constructor and an assignment operator, whenever you make a copy of a Foo, both the original and the copy will use a pointer to the same int; when either the original or the copy gets destroyed, the pointer is freed, leaving the other with unusable data.

Foo(cont Foo& other) {
    other.ptr_ = new int(*ptr_);
}

// Same for operator=

If you don't do any dynamic allocations in your constructor/ destructor, there's a good chance you don't actually need a copy constructor or an assignment operator (but not necessarily).