c++ importance - Will an 'empty' constructor or destructor do the same thing as the generated one?




of example (7)

Suppose we have a (toy) C++ class such as the following:

class Foo {
    public:
        Foo();
    private:
        int t;
};

Since no destructor is defined, a C++ compiler should create one automatically for class Foo. If the destructor does not need to clean up any dynamically allocated memory (that is, we could reasonably rely on the destructor the compiler gives us), will defining an empty destructor, ie.

Foo::~Foo() { }

do the same thing as the compiler-generated one? What about an empty constructor -- that is, Foo::Foo() { }?

If there are differences, where do they exist? If not, is one method preferred over the other?


Answers

I know I'm late in the discussion, nevertheless my experience says that the compiler behaves differently when facing an empty destructor compared to a compiler generated one. At least this is the case with MSVC++ 8.0 (2005) and MSVC++ 9.0 (2008).

When looking at the generated assembly for some code making use of expression templates, I realized that in release mode, the call to my BinaryVectorExpression operator + (const Vector& lhs, const Vector& rhs) was never inlined. (please don't pay attention to the exact types and operator signature).

To further diagnose the problem, I enabled the various Compiler Warnings That Are Off by Default. The C4714 warning is particularly interesting. It is emitted by the compiler when a function marked with __forceinline doesn't get inlined nonetheless.

I enabled the C4714 warning and I marked the operator with __forceinline and I could verify the compiler reports it was unable to inline the call to the operator.

Among the reasons described in the documentation, the compiler fails to inline a function marked with __forceinline for:

Functions returning an unwindable object by value when -GX/EHs/EHa is on

This is the case of my BinaryVectorExpression operator + (const Vector& lhs, const Vector& rhs). BinaryVectorExpression is returned by value and even though its destructor is empty, it makes this return value being considered as an unwindable object. Adding throw () to the destructor didn't help the compiler and I avoid using exception specifications anyway. Commenting out the empty destructor let the compiler fully inline the code.

The take-away is that from now, in every class, I write commented out empty destructors to let humans know the destructor does nothing on purpose, the very same way people comment out the empty exception specification `/* throw() */ to indicate that the destructor cannot throw.

//~Foo() /* throw() */ {}

Hope that helps.


I agree with David except that I would say it is generally a good practice to define a virtual destructor i.e.

virtual ~Foo() { }

missing out virtual destructor can lead to memory leak because people who inherit from your Foo class may not have noticed that their destructor will never be called!!


I'd say best to put the empty declaration, it tells any future maintainers that it wasn't an oversight, and you really did mean to use the default one.


An empty definition is fine since the definition can be referenced

virtual ~GameManager() { };
The empty declaration is deceptively similar in appearance
virtual ~GameManager();
yet invites the dreaded no definition for virtual destructor error
Undefined symbols:
  "vtable for GameManager", referenced from:
      __ZTV11GameManager$non_lazy_ptr in GameManager.o
      __ZTV11GameManager$non_lazy_ptr in Main.o
ld: symbol(s) not found

The empty destructor that you defined out of class has similar semantics in most regards, but not in all.

Specifically, the implicitly defined destructor
1) is an inline public member (yours is not inline)
2) is denoted as a trivial destructor (necessary to make trivial types that can be in unions, yours cannot)
3) has an exception specification (throw(), yours does not)


Yes, that empty destructor is the same as the automatically-generated one. I've always just let the compiler generate them automatically; I don't think it's necessary to specify the destructor explicitly unless you need to do something unusual: make it virtual or private, say.


Interfaces are light weight way to enforce a particular behavior. That is one way to think of.





c++ class oop constructor destructor