Currently I met with the following situation:
I have a base class Base
with an void* V
member, it may be A*, B*, C*
actually, and three (only three, fixed number) catagories of class will derive from Base
, each will fill V
will different kinds of data. I don't want the user to manually free V
in the destructor of every derived class, then Base
must determine the real type of V
and call the destructor of it. But since virual functions are treated as non-virtual, how can I get the type of V
? it will occupies more memory if I added a _type
variable additionally (and it will force the user to fill _type
for every constructor.
Example here:
class Base {
public:
void* V;
virtual ~Base() { /* how to free V? */ }
};
class A : public Base {
public:
A() : V((void*) new int) {}
virtual ~A() { /* I don't want to let user
write delete (int*) V in every destructor of
class of type 1, since the user may forget. */
virtual int type() const { return 1; }
};
class B : public Base {
public:
B() : V((void*) new double) {}
virtual int type() const { return 2; }
};
class C : public Base {
public:
C() : V((void*) new int[4]) {}
virtual int type() const { return 3; }
};
My main purpose is to do more things for the users, and don't let them to manually manager the memeory, so for this situation, is there a better way to achieve the same goal? Or is there a workaround to free V
properly?
If I understand you correctly, you have a hirarchy of entities where the derived ones(A
, B
, etc.) each holding different data types.
The simplest solution would be to remove V
from Base
and add a specific data member (without a pointer) to each derived class. This way you don't have to deal with deletion of V
at all.
If for some reason you want to separate the main entities from the data they are holding (with the base having access to it), you can create a separate hirarchy of data entities (BaseData
, AData
, BData
etc.). You can also use a smart pointer to avoid the manual memory management.
This is demonstrated below:
#include <memory>
#include <array>
// Hirarchy of data entities:
struct BaseData {
virtual ~BaseData() = default; // required for proper destruction of derived classes
};
struct AData : public BaseData {
int m_data{ 0 };
};
struct BData : public BaseData {
double m_data{ 0 };
};
struct CData : public BaseData {
std::array<int, 4> m_data{ 0 };
};
// Hirarchy of main entities:
class Base {
public:
std::unique_ptr<BaseData> m_V;
virtual ~Base() = default; // required for proper destruction of derived classes
};
class A : public Base {
public:
A() { m_V = std::make_unique<AData>(); }
};
class B : public Base {
public:
B() { m_V = std::make_unique<BData>(); }
};
class C : public Base {
public:
C() { m_V = std::make_unique<CData>(); }
};
Notes:
std::array
instead of a raw C array.structs
only for simplicity.Base
accepting a std::unique_ptr<BaseData>
in order to make sure the derived classes will initialize m_V
. m_V
can also be made private while allowing to access it via an accessor method.