Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

The rule of 3 (the rule of 5 in the new c++ standard) states :

If you need to explicitly declare either the destructor, copy constructor or copy assignment operator yourself, you probably need to explicitly declare all three of them.

But, on the other hand, the Martin's "Clean Code" advises to remove all empty constructors and destructors (page 293, G12:Clutter) :

Of what use is a default constructor with no implementation? All it serves to do is clutter up the code with meaningless artifacts.

So, how to handle these two opposite opinions? Should empty constructors/destructors really be implemented?


Next example demonstrates exactly what I mean :

#include <iostream>
#include <memory>

struct A
{
    A( const int value ) : v( new int( value ) ) {}
    ~A(){}
    A( const A & other ) : v( new int( *other.v ) ) {}
    A& operator=( const A & other )
    {
        v.reset( new int( *other.v ) );
        return *this;
    }

    std::auto_ptr< int > v;
};
int main()
{
    const A a( 55 );
    std::cout<< "a value = " << *a.v << std::endl;
    A b(a);
    std::cout<< "b value = " << *b.v << std::endl;
    const A c(11);
    std::cout<< "c value = " << *c.v << std::endl;
    b = c;
    std::cout<< "b new value = " << *b.v << std::endl;
}

Compiles fine using g++ 4.6.1 with :

g++ -std=c++0x -Wall -Wextra -pedantic example.cpp

The destructor for struct A is empty, and not really needed. So, should it be there, or should it be removed?

share|improve this question
13  
The 2 quotes talk about different things. Or I totally miss your point. –  Benjamin Bannier Apr 12 '12 at 11:18
1  
@honk In the coding standard of my team, we have a rule to always declare all 4 (constructor, destructor, copy constructors). I was wondering if it really makes sense to do. Do I really have to always declare destructors, even if they are empty? –  BЈовић Apr 12 '12 at 12:35
    
As for empty desctructors think about this: codesynthesis.com/~boris/blog/2012/04/04/…. Otherwise the rule of 3(5) makes perfect sense to me, no idea why one would want a rule of 4. –  Benjamin Bannier Apr 12 '12 at 12:40
    
@honk Watch out about information you find on the net. Not all is true. For example, virtual ~base () = default; doesn't compile (with a good reason) –  BЈовић Apr 12 '12 at 13:04
    
@VJovic, No you don't have to declare an empty destructor, unless you need to make it virtual. And while we are on the subject, you should not be using auto_ptr either. –  Dima Apr 12 '12 at 20:23

5 Answers 5

up vote 32 down vote accepted

For a start the rule says "probably", so it doesn't always apply.

The second point I see here is that if you have to declare one of the three, that's because it's doing something special like allocating memory. In this case, the others wouldn't be empty since they would have to handle the same task (such as copying the content of dynamically allocated memory in the copy constructor or freeing such memory).

So as a conclusion, you shouldn't declare empty constructors or destructors, but it's very likely that if one is needed, the others are needed too.

As for your example: In such a case, you can leave the destructor out. It does nothing, obviously. Usage of smart pointers is a perfect example of where and why the rule of 3 doesn't hold.

It's just a guide for where to take a second look over your code in case you may have forgotten to implement important functionality you might otherwise have missed.

share|improve this answer
    
With the use of smart pointers, the destructors are empty in most cases (I'd say >99% of destructors in my code base is empty, because almost every class uses the pimpl idiom). –  BЈовић Apr 12 '12 at 12:39
    
Wow, that's so much pimpling I'd call it smelly. With many compilers pimpled will be harder to optimize (e.g. harder to inline). –  Benjamin Bannier Apr 12 '12 at 12:46
    
@honk What do you mean by "many compilers pimpled"? :) –  BЈовић Apr 12 '12 at 12:50
    
@VJovic: sorry, typo: 'pimpled code' –  Benjamin Bannier Apr 12 '12 at 12:52

Here your potential (*) equivalent to the default one constructor/assignment/destructor has a purpose: document the fact you have though about the issue and determined that the default behavior was correct. BTW, in C++11, things have not stabilized enough to know if =default can serve that purpose.

(There is another potential purpose: provide an out of line definition instead of the default inline one, better to document explicitly if you have any reason to do so).

(*) Potential because I don't remember a real life case where the rule of three didn't apply, if I had to do something in one, I had to do something in the others.


Edit after your addition of an example. your example using auto_ptr is interesting. You are using a smart pointer, but not one which is up to the job. I'd rather write one which is -- especially if the situation occurs often -- than doing what you did. (If I'm not mistaken, neither the standard nor boost provide one).

share|improve this answer
    
The example demonstrates my point. The destructor is not really needed, but the rule of 3 tells it should be there. –  BЈовић Apr 12 '12 at 19:48

There is really no contradiction here. The rule of 3 talks about the destructor, the copy constructor and the copy assignment operator. Uncle Bob talks about empty default constructors.

If you need a destructor, then your class probably contains pointers to dynamically allocated memory, and you probably want to have a copy ctor and an operator=() that do a deep copy. This is completely orthogonal to whether or not you need a default constructor.

Note also that in C++ there are situations when you do need a default constructor, even if it is empty. Let's say your class has a non-default constructor. In that case, the compiler will not generate a default constructor for you. That means that objects of this class cannot be stored in STL containers, because those containers expect the objects to be default-constructable.

On the other hand, if you are not planning to ever put the objects of your class into STL containers, an empty default constructor certainly is useless clutter.

share|improve this answer

The rule of 5 is a cautalative extension of the rule of 3 that is a cautelative behavior againt possible object misuse.

If you need to have a destructor, it means that you did some "resource management" other than the default (just construct and destruct values).

Since copy, assign, move and transfer by default copy values, if you are not holding just values, you have to define what to do.

That said, C++ deletes teh copy if you define the move and deletes the move if you define the copy. In the most of the case you have to define if you want to emulate a value (hence copy mut clone the resource, and move has no sense) or a resource manager (and hence move the resource, where copy has no sense: the rule of 3 becomes the rule of the other 3)

The cases when you have to define both copy and move (rule of 5) are quite rare: typically you have "big value" that has to be copyed if given to distinct objects, but can be moved if taken from a temporary object (avoiding a clone then destroy). That's the case for STL containers or arithmetic containers.

A case can be matrixes: they have to support copy because they are values, (a=b; c=b; a*=2; b*=3; must not influence each other) but they can be optimized by supporting also moving ( a = 3*b+4*c has a + that takes two temporaries and generates a temporary: avoiding clone and delete can be useful)

share|improve this answer

There is another point not mentioned in the discussion yet: A destructor should always be virtual.

struct A
{
    A( const int value ) : v( new int( value ) ) {}
    virtual ~A(){}
    ...
}

The constructor needs to be declared as virtual in the base class to make it virtual in all derived classes too. So, even if your base class does not need a destructor, you end up declaring and implementing an empty destructor.

If you put on all warnings on (-Wall -Wextra -Weffc++) g++ will warn you about this. I consider it good practice to always declare a virtual destructor in any class, because you never know, if your class will eventually become a base class. If the virtual destructor is not needed, it does no harm. If it is, you save time to find the error.

share|improve this answer
    
But I don't want the virtual constructor. If I do that, then every call to any method would use virtual dispatch. btw take a note that there are no such thing as "virtual constructor" in c++. Also, I compiled the example as very high warning level. –  BЈовић Apr 14 '12 at 7:07

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.