Inheritance without virtual destructor

I have two classes that are used in a project. One class Callback

,, is responsible for storing information from the callback. Another class UserInfo

is information that is presented to the user. Basically, it UserInfo

should have been a very thin shell that reads data Callback

and transmits it to the user, and also provides additional material.

struct Callback {
  int i;
  float f;
};

struct UserInfo {
  int i;
  float f;

  std::string thekicker;
  void print();
  UserInfo& operator=(const Callback&);
};

      

The problem is that adding members to Callback

requires identical changes to UserInfo

as well as updating operator=

and similarly dependent member functions. To sync them automatically, I want to do this instead:

struct Callback {
  int i;
  float f;
};

struct UserInfo : Callback{
  std::string thekicker;
  void print();
  UserInfo& operator=(const Callback&);
};

      

UserInfo

Will now have all the same items as Callback

. In fact, the kicker is a data member thekicker

. There Callback

are no virtual destructors, and I believe other coders want it to stay that way (they strongly feel against the performance penalty for virtual destructors). However, there thekicker

will be a leak if the type UserInfo

is destroyed via Callback*

. It should be noted that it was not intended to UserInfo

ever be used through an interface Callback*

, which is why these classes were separate. On the other hand, having to change three or more pieces of code in the same way just to change one structure feels inelegant and error prone.

Question . Is it possible to allow UserInfo

inheritance in Callback

public (users should have access to all the same information), but disallow assignment of the reference to Callback

a UserInfo

, in particular due to the lack of a virtual destructor? I suspect this is not possible, since this is the main purpose for inheritance in the first place. My second question is, is there a way to keep these two classes in sync with each other using a different method? I wanted to make it a Callback

member UserInfo

instead of the parent class, but I want the data members to be directly read using user.i

instead user.call.i

.

I think I am asking for the impossibility, but I am constantly amazed at the witchcraft of stackoverflow answers, so I thought I would just ask to see if this is really a remedy for this.

+3


source to share


4 answers


You can always enforce the 'can not delete via base class pointer' constraint you mentioned (to some extent) by making the destructor protected in the base class:

i.e.

// Not deletable unless a derived class or friend is calling the dtor.
struct Callback {
  int i;
  float f;
protected:
  ~Callback() {}
};

// can delete objects of this type:
struct SimpleCallback : public Callback {};


struct UserInfo : public Callback {
  std::string thekicker;
  // ...
};

      



As mentioned, you can remove the assignment operator. For pre-C ++ 11, just make the unfinished prototype of this function private:

private:
  UserInfo& operator=(const Callback&);

      

+3


source


struct UserInfo : Callback {
  ...
  // assignment from Callback disallowed
  UserInfo& operator=(const Callback&) = delete;
  ...
};

      

Please note that STL has a lot of inheritance without a virtual destructor. The documentation explicitly states that these classes are not intended to be used as base classes.



some examples are vector <>, set <>, map <> ....

Another approach is to consider private inheritance when providing an accessor to expose the callback (in which case you can use encapsulation, which is cleaner).

+1


source


Yes, there is a trickery you can use to keep items in sync and update operator=

automatically. This is ugly, although it includes macros and an unusual way of using the include file.

CallBackMembers.h:

MEMBER(int, i)
MEMBER(float, f)

      

In the other place:

struct Callback {
  #define MEMBER(TYPE,NAME) TYPE NAME;
  #include "CallbackMembers.h"
  #undef MEMBER
};

struct UserInfo {
  #define MEMBER(TYPE,NAME) TYPE NAME;
  #include "CallbackMembers.h"
  #undef MEMBER

  std::string thekicker;

  void print();    // you can use the macro trick here too

  UserInfo& operator=(const Callback& rhs)
  {
    #define MEMBER(TYPE,NAME) NAME = rhs.NAME;
    #include "CallbackMembers.h"
    #undef MEMBER
    return *this;
  }
};

      

0


source


It is not possible to fulfill ALL the criteria you want.

Personally, I think your idea of ​​making it a member and then using it user.call.i

is the best and most understandable option. Keep in mind that you are writing code that only uses this once, but you make up for it in maintainability (since yours UserData

should never change) and readability (since it is 100% transparent to end use which attribute is part of the callback data and which are auxiliary).

The only other option that might make sense is to use private inheritance instead, and the using

attribute or function in UserData

. That being said, you still have to add one using

when the new data is added to the callback, but you get the desired syntax user.i

for the clients.

0


source







All Articles