Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a class hierarchy be safe and trivially copyable?

Tags:

c++

We use a framework that relies on memcpy in certain functions. To my understanding I can give everything that is trivially copyable into these functions.

Now we want to use a simple class hierarchy. We are not sure whether we can have a class hierarchy that results in trivially copyable types because of the safe destruction. The example code looks like this.

class Timestamp; //...

class Header
{
public:
  uint8_t Version() const;
  const Timestamp& StartTime();
  // ... more simple setters and getters with error checking

private:
  uint8_t m_Version;
  Timestamp m_StartTime;
};

class CanData : public Header
{
public:
  uint8_t Channel();
  // ... more setters and getters with error checking

private:
  uint8_t m_Channel;
};

The base class is used in several similar subclasses. Here I omitted all constructors and destructors. Thus the classes are trivially copyable. I suppose though that the user can write a code that results in a memory leak like this:

void f()
{
  Header* h = new CanData();
  delete h;
}

Is it right that the class hierarchy without the virtual destructor is a problem even if all classes use the compiler's default destructor? Is it therefore right that I cannot have a safe class hierarchy that is trivially copyable?

like image 737
Simon Siemens Avatar asked May 06 '15 16:05

Simon Siemens


Video Answer


2 Answers

This code

Header* h = new CanData();
delete h;

will trigger undefined behavior since §5.3.5/p3 states:

In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined

and regardless of not having dynamically allocated objects contained in your derived class (really bad if you have), you shouldn't do it. Having a class hierarchy without the base class virtual destructor is not a problem per se, it becomes a problem when you try to mix static and dynamic types with delete.

Doing memcpy on a derived class object smells of bad design to me, I would rather address the need for a "virtual constructor" (i.e. a virtual clone() function in your base class) to duplicate your derived objects.

You can have your class hierarchy that is trivially copyable if you make sure that your object, its subobjects and base classes are trivially copyable. If you want to prevent users referring to your derived objects via base classes you could, as Mark first suggested, render the inheritance protected

class Header
{
public:
};

class CanData : protected Header
{               ^^^^^^^^^
public:
};

int main() {
  Header *pt = new CanData(); // <- not allowed
  delete pt;
}

Notice that you won't be able to use base pointers at all to refer to derived objects due to §4.10/p3 - pointer conversions.

like image 186
Marco A. Avatar answered Oct 21 '22 03:10

Marco A.


If you delete a pointer to a derived type held as its base type and you don't have a virtual destructor, the derived types destructor won't be called, whether it's implicitly generated or not. And whether its implicitly generated or not, you want it to be called. If the derived type's destructor wouldn't actually do anything anyway though, it might not leak anything or cause a problem. If the derived type holds something like a std::string, std::vector, or anything with a dynamic allocation, you want the dtors to be called. As a matter of good practice, you always want a virtual destructor for base classes whether or not the derived classes destructors need to be called (since a base class shouldn't know about what derives from it, it shouldn't make an assumption like this).

If you copy a type like so:

Base* b1 = new Derived;
Base b2 = *b1;

You will only invoke Bases copy ctor. The parts of the object which are actually from Derived will not be involved. b2 will not secretly be a Derived, it will just be a Base.

like image 32
David Avatar answered Oct 21 '22 04:10

David