design and initialization of data in derived classes - c++

I have the following hierarchy:
Base_class
|
Traits_class
|
Concrete_class
Now the thing is that the data is contained in the Base_class (it needs to be there because the Traits_class has to have access to it. Traits_class is a class template that has different functionality depending on the template parameter passed (so I use partial template specialization for the different classes). Finally, at the lowest level, the Concrete_class is also a class template. I create instances of Concrete_class only.
Now the question is: I have written all constructors, the destructor and I have provided move semantics within the Concrete_class. That means that I do not call the base constructors, but I initialize the state directly in the derived classes.
Can someone point out if there is a problem with this? Only the destructor is declared in the Base_class, and declared as protected. Is there an evident flaw in this design?
Thanks for your insight!
EDIT
So I revised the design following Yakk's comment on the CRTP, and now I have
Traits_class
|
Concrete_class
I have also moved all data to the Concrete_class, and thanks to CRTP I can have access to it in the Traits_class. Something weird happened though, as I couldn't access the data in the Traits_class within the constructor of the Traits_class. I mean, I did access to it, but it seemed as if I was accessing ghost data, because I initialized the members in Traits_class (and even printed within the Traits_class constructor), but then just afterwards the class was empty. So I really don't understand what happened (I was const_casting the Traits_class into the Concrete_class to do this).
In the end, I wrote just static member functions in the Traits_class to initialize the members of the Concrete_class. I guess I could have used protected member functions to do the same thing (because I'm inheriting from Traits_class), but I believe it's the same thing.
If you have any further comments, please let me know. And thanks again for your C++ wisdom.
aa

There is an error in your reasoning. A constructor always initializes all base classes and nonstatic members (technically, a virtual base is initialized by the most-derived type and not by any of the other bases' constructors), so the Base_class will actually be initialized by its compiler-generated default constructor (or compiler-generated copy/move constructors if you're doing a copy or move), which will initialize all data members using their default (or copy/move) constructors. You may later assign those members in the concrete class's constructors, but initialization has already occurred by this point.
Since the base class owns all the data members, it will actually be the base class that initializes all data members when a copy or move occurs. If you write your own copy or move constructor in the most-derived class, you'll either need to invoke the base class's copy/move constructor in your initialization list or the data members will be default-constructed and you'll be forced to use a copy/move-assignment after the fact. This is often inefficient and in some cases may be incorrect. (For example, I've written classes that could be move-constructed, but could not be move-assigned because of slicing issues; if you had such a class in your Base_class as a data member, you couldn't implement move semantics solely in the Concrete_class.)
If you must initialize all the data members from Concrete_class, I recommend you provide a protected constructor in Base_class that takes all data members by value and moves them into its own data members, and provide a perfect-forwarding constructor in Traits_class (or inherit the base's constructor, if you are using a compiler with this support). This allows the concrete class to specify the values for initializing the data members, but allows the base class to do the actual initialization. It also allows the Base_class and Traits_class constructors access to the fully initialized data members (whereas otherwise they can only access the data members in their default-initialized state).
Here's an example:
struct Base_class {
protected:
Base_class( string s ) : s_( move(s) ) { }
~Base_class() = default;
// Request copy/move, since we're declaring our own (protected) destructor:
Base_class(Base_class const &) = default;
Base_class(Base_class &&) = default;
Base_class &operator=(Base_class const &) = default;
Base_class &operator=(Base_class &&) = default;
string s_;
};
template<int>
struct Traits_class : Base_class {
protected:
template<typename... P>
Traits_class( P &&p )
: Base_class( forward<P>(p)... )
{ }
};
template<int I>
struct Concrete_class : Traits_class<I> {
Concrete_class( char c )
: Traits_class<I>( string( I, c ) )
{ }
};
As a side-note, the data doesn't necessarily have to be in Base_class for Traits_class to be able to access it. You could provide access via a protected virtual function if you don't mind the overhead of a virtual function call and if you don't need access within the constructors or destructor (which I'm assuming you don't, since currently the data members don't have their final values until after Concrete_class's constructor runs). Or, to avoid the virtual call overhead, you could use the Curiously Recurring Template Pattern as #Yakk mentions.
== RESPONSE TO EDIT IN ORIGINAL QUESTION ==
The base class's constructor will run before the derived class's constructor, so if the data is stored in the derived class, it will be uninitialized in the base class's constructor (and already reclaimed in its destructor). You can think of a constructor as taking an instance of base class and turning it into an instance of derived class (by initializing the derived part of the class, etc.), and as a special case, the constructor for a class with no base classes turns "nothing" (raw storage) into an instance of the class.
So, when your traits class constructor is running, it isn't yet a concrete derived class. Consequently, accessing the derived class's data members or otherwise using the class as a derived class is illegal. The language even enforces this for virtual functions; if you call a virtual function inside a base class's constructor or destructor, it will call the base version of the function. Example:
#include <iostream>
using namespace std;
struct Base {
Base() { cout << "Base ctor\n"; v(); }
~Base() { v(); cout << "Base dtor\n"; }
protected:
virtual void v() const { cout << "Base::v\n"; }
};
struct Derived : Base {
Derived() { cout << "Derived ctor\n"; v(); }
~Derived() { v(); cout << "Derived dtor\n"; }
protected:
virtual void v() const { cout << "Derived::v\n"; }
};
int main() { Derived d; }
/* Output:
Base ctor
Base::v
Derived ctor
Derived::v
Derived::v
Derived dtor
Base::v
Base dtor
*/

Related

Avoid constructor with empty base class with virtual method

I have an empty base class with a virtual function. Is there any way I can avoid manually implementing the constructors of Derived in order to be able to initialize it?
struct Base
{
virtual int f(int) const = 0;
virtual ~Base() = 0;
};
Base::~Base() {}
struct Derived: public Base
{
int v;
int f(int) const override{ return v;};
};
int main()
{
return Derived{5}.f(3);
}
Is there any way I can avoid manually implementing the constructors of Derived in order to be able to initialize it?
No. Having a base class and virtual functions causes Derived not being an aggregate.
If you remove the virtual functions, you have aggregates and you can
return Derived{{}, 5}.f(3);
// ^^ explicitly initialize base class
but as this is not the point of your question, this is no solution to it.
Both the base class and the derived class has an implicit default constructor (as well as copy and move), and there is no need to manually implement those.
Neither class is an aggregate, so they cannot be initialised using list initialisation without implementing a custom constructor that accepts compatible list of arguments. This is why your example program does not work. Neither class can become an aggregate without removing the virtual member function.
So, your options are:
Don't use list initialisation, except with an empty list of parameters which is a special case that performs value initialisation which invokes the default constructor
or provide a custom constructor for the list of arguments that you want to pass
or remove the virtual member function so that the classes become aggregates, and their members can then be initialised directly with list initialisation

order of calling constructor in inheritance

I am new to C++ programming language, i have a confusion about order of calling the constructor in inheritance. my question is even though the constructor and destructor are not inherited by derived class why the base class constructor will call when i create a derived class object.
The purpose of a constructor is to define how the data members should be initialised. Since a derived class inherits the data members from the base class, any constructor of the derived class must define not only how to initialise the data members that are specific to the derived class, but also those coming from the base class.
The natural way to do this (and required by the C++ Standard) is by calling a base class constructor. If you don't include a specific constructor call in the initialization list for your derived-class constructor, the default constructor of the base class will be used to initialise the base-class members.
struct base
{
int _i; // a data member
base():_i(0) {} // default constructor
base(int i):_i(i) {} // special constructor
};
struct derived : base
{
int _j; // a data member specific to the derived class
derived(int i, int j):_j(j) {} // user-defined constructor for the derived class
};
The example above illustrates how the derived-class constructor can initialise its member _j, but the member _i, coming from the base class, must be initialised using a base-class constructor.
The way it's written above, the default constructor base::base() will be automatically called by the compiler, i.e. _i will be initialised to 0.
But you can change that behaviour by including a call to a specific constructor:
struct derived : base
{
int _j;
derived(int i, int j):base(i),_j(j) {} // user-defined constructor for the derived class
};
The order of the constructor calls is: First constructor calls of the base class, then initializers for the derived-class specific members. This is only natural because the derived class, in a sense, is an extension of the base class, and it makes sense to regard the base-class part of the derived-class object as being there first.

Why does a purely virtual/abstract class require a constructor, in particular for protected const member variables?

I have a purely virtual class defined as such:
class BaseClass {
protected:
const int var;
public:
void somefun() = 0; // what I mean by a purely virtual class
// stuff...
};
If I don't add a constructor defined as such:
BaseClass(const int & VAR) : var(VAR) {};
that I would have to subsequently use in ever derived class, my derived class can't initialize the const variable var to whichever value it wants to. Now I actually understand what's going on here. Before constructing a derived class, a constructor of the base class is called, at which point const member variables must be initialized. My question is not a "how do I make my code work" kind of question, that's already been done. My question is about why the compiler thinks it's necessary. For a purely virtual class, shouldn't I be allowed to write something like:
class DerivedClass : BaseClass {
public:
DerivedClass() : var(SOME_VALUE) {};
}
If the compiler knows that a call to a BaseClass constructor will necessarily be followed by a call to some derived class constructror (since an object of abstract type can never be instantiated) shouldn't it give us a bit more leeway?
Is this all a consequence of how C++ chooses to get around the Diamond problem? Even if that was the case, shouldn't the compiler at least somehow allow for the possibility that const member variable of purely virtual functions will be defined in derived classes? Is that too complicated or does that mess with the C++ solution to the Diamond problem?
Thanks for the help everyone.
It's not "purely virtual" (whatever you mean by that) - it contains a data member.
Class members can only be initialised by the initialiser list of a constructor of that class, not of a derived class. That's how object initialisation is specified: all members that are initialised, are initialised before the constructor body begins.
Constant objects must be initialised, since they can't be assigned a value later.
Therefore, a class with a constant data member must initialise it in each constructor.
For a purely virtual class, shouldn't I be allowed to write something
like
No, but you can(and in this case should) write something like this:
class DerivedClass : BaseClass {
public:
DerivedClass() : BaseClass(SOME_VALUE) {};
};
The construction of an object occurs in a specific order. The base class must be fully constructed before the constructor of a derived class is run, so that the derived constructor is working with a fully formed and valid base object. If the initialization of base member variables were put off until the construction of the derived class, this invariant would be broken.

C++ Type changes during derived class construction - virtual function question

C++ type change during deletion
I've read that when you construct a derived type, the type changes depending on which constructor is being called. So, if you create a derived object and call a virtual function using a base pointer, normally it would map to the implementation in the derived class. If you called the virtual function in the base class constructor though, it would use the base class implementation as the type of the object is technically that of the base class while in that function. For example (makeshift code, sorry if it doesn't compile):
class Base {
Base()
{
std::cerr << "Base Constructor.";
func();
}
virtual void func() {
std::cerr << "Func base called." << std::endl;
}
};
class Derived : public Base {
Derived()
{
std::cerr << "Derived Constructor.";
func();
}
void func() {
std::cerr << "Func derived called." << std::endl;
}
};
int main() {
Derived* d = new Derived;
delete d;
}
Should output:
Base Constructor.
Func base called.
Derived Constructor.
Func derived called.
First of all, is this always true or is it implementation dependent?
If I used RTTI and typeinfo, would the type printed in the base actually be that of the base, or is this more of an unwritten rule sort of situation?
Is it dangerous to call virtual functions from constructors with this in mind, or is it safe as long as you know what you're doing?
To keep it short and simple, you can have a Rule:
The virtual mechanism is disabled in Constructors and Destructors
A virtual function call in Base class will always call the base class version of the function, the same in derived class results in call to the Derived class version of the function.
First of all, is this always true or is it implementation dependent?
Yes this is always true. This is not implementation-dependent.
If I used RTTI and typeinfo, would the type printed in the base actually be that of the base?
Yes it would be of Base; Derived object doesn't even exist while you are in Base class constructor.
Is it dangerous to call virtual functions from constructors with this in mind, or is it safe as long as you know what you're doing?
No it is not dangerous to call virtual functions from constructor as long as you understand the semantics behind it.
This C++ FAQ should be a good read for you.
It's well-defined.
[n3290: 12.7/4]: Member functions, including virtual functions
(10.3), can be called during construction or destruction (12.6.2).
When a virtual function is called directly or indirectly from a
constructor or from a destructor, including during the construction or
destruction of the class’s non-static data members, and the object to
which the call applies is the object (call it x) under construction or
destruction, the function called is the final overrider in the
constructor’s or destructor’s class and not one overriding it in a
more-derived class. If the virtual function call uses an explicit
class member access (5.2.5) and the object expression refers to the
complete object of x or one of that object’s base class subobjects but
not x or one of its base class subobjects, the behavior is undefined.
There is an excellent article from Scott Meyers. It is from his book Effective C++.
The article can be found at:
Never Call Virtual Functions during Construction or Destruction
It also discusses an alternative implementation.
Recently I had a similar problem which I solved this way:
class EthernetFrame
{
protected:
/** ctor to be called from derived classes */
EthernetFrame(unsigned inPayloadLength)
{
calculatePadBytes(inPayloadLength);
}
private:
/** calculates needed required PadBytes for Frames < 64B
* #param inPayloadLength we need to know the length of the actual L3 frame
*/
void calculatePadBytes(unsigned inPayloadLength);
};
class IPv4Frame : public EthernetFrame
{
public:
/** create empty IPv4 packet */
IPv4Frame() :
EthernetFrame(cIPv4_MINIMUM_LENGTH)
{};
// IPv4 header + trailer in bytes
unsigned cIPv4_MINIMUM_LENGTH;
protected:
/** ctor to be called from derived classes */
IPv4Frame(unsigned inPayloadLength) :
EthernetFrame(cIPv4_MINIMUM_LENGTH+inPayloadLength)
{};
};

Why base class destructor (virtual) is called when a derived class object is deleted?

A difference between a destructor (of course also the constructor) and other member functions is that, if a regular member function has a body at the derived class, only the version at Derived class gets executed. Whereas in case of destructors, both derived as well as base class versions get executed?
It will be great to know what exactly happens in case of destructor (maybe virtual) & constructor, that they are called for all its base classes even if the most derived class object is deleted.
Thanks in advance!
The Standard says
After executing the body of the destructor and destroying any automatic objects allocated within the body,
a destructor for class X calls the destructors for X’s direct non-variant members,the destructors for X’s direct
base classes and, if X is the type of the most derived class (12.6.2), its destructor calls the destructors for
X’s virtual base classes. All destructors are called as if they were referenced with a qualified name, that is,
ignoring any possible virtual overriding destructors in more derived classes. Bases and members are destroyed
in the reverse order of the completion of their constructor (see 12.6.2). A return statement (6.6.3) in a
destructor might not directly return to the caller; before transferring control to the caller, the destructors
for the members and bases are called. Destructors for elements of an array are called in reverse order of
their construction (see 12.6).
Also as per RAII resources need to be tied to the lifespan of suitable objects and the destructors of respective classes must be called upon to release the resources.
For example the following code leaks memory.
struct Base
{
int *p;
Base():p(new int){}
~Base(){ delete p; } //has to be virtual
};
struct Derived :Base
{
int *d;
Derived():Base(),d(new int){}
~Derived(){delete d;}
};
int main()
{
Base *base=new Derived();
//do something
delete base; //Oops!! ~Base() gets called(=>Memory Leak).
}
Constructor and destructor are different from the rest of regular methods.
Constructor
can't be virtual
in derived class you either call explicitly constructor of base class
or, in case where you don't call base class constructor compiler will insert the call. It will call the base constructor without parameters. If no such constructor exists then you get compiler error.
struct A {};
struct B : A { B() : A() {} };
// but this works as well because compiler inserts call to A():
struct B : A { B() {} };
// however this does not compile:
struct A { A(int x) {} };
struct B : A { B() {} };
// you need:
struct B : A { B() : A(4) {} };
Destructor:
when you call destructor on derived class over a pointer or a reference, where the base class has virtual destructor, the most derived destructor will be called first and then the rest of derived classes in reversed order of construction. This is to make sure that all memory has been properly cleaned. It would not work if the most derived class was called last because by that time the base class would not exists in memory and you would get segfault.
struct C
{
virtual ~C() { cout << __FUNCTION__ << endl; }
};
struct D : C
{
virtual ~D() { cout << __FUNCTION__ << endl; }
};
struct E : D
{
virtual ~E() { cout << __FUNCTION__ << endl; }
};
int main()
{
C * o = new E();
delete o;
}
output:
~E
~D
~C
If the method in base class is marked as virtual all the inherited methods are virtual as well so even if you don't mark the destructors in D and E as virtual they will still be virtual and they still get called in the same order.
This is by design. The destructor on the base class must be called in order for it to release its resources. Rule of thumb is that a derived class should only clean up its own resources and leave the base class to clean up itself.
From C++ spec:
After executing the body of the
destructor and destroying any
automatic objects allocated within the
body, a destructor for class X calls
the destructors for X’s direct
members, the destructors for X’s
direct base classes and, if X is the
type of the most derived class
(12.6.2), its destructor calls the
destructors for X’s virtual base
classes. All destructors are called as
if they were referenced with a
qualified name, that is, ignoring any
possible virtual overriding
destructors in more derived classes.
Bases and members are destroyed in the
reverse order of the completion of
their constructor (see
12.6.2).
Also, because there is only one destructor, there is no ambiguity as to which destructor a class must call. This is not the case for constructors, where a programmer must pick which base class constructor should be called if there isn't an accessible default constructor.
Because that's how dtor's work. When you create an object, ctors are invoked starting from the base, and going all the way to the most derived. When you destroy objects (correctly) the reverse happens. The time that making a dtor virtual makes a difference is if/when you destroy an object via a pointer (or reference, though that's fairly unusual) to the base type. In that case, the alternative isn't really that only the derived dtor gets invoked -- rather, the alternative is simply undefined behavior. That make happen to take the form of invoking only the derived dtor, but it might take an entirely different form as well.
A base class destructor may be responsible for cleaning up resources that were allocated by the base class constructor.
If your base class has a default constructor (one that doesn't take parameters or has defaults for all its parameters) that constructor is automatically called upon construction of a derived instance.
If your base class has a constructor that requires parameters, you must call it manually in the initializer list of the derived class constructor.
Your base class destructor will always be automatically called upon deletion of the derived instance since destructors don't take parameters.
If you're using polymorphism and your derived instance is pointed to by a base class pointer, then the derived class destructor is only called if the base destructor is virtual.
As Igor says constructors must be called for base classes. Consider what would happen if it wouldn't be called:
struct A {
std::string s;
virtual ~A() {}
};
struct B : A {};
If the destructor for A would not be called when deleting a B instance, A would never be cleaned up.
When any object is destroyed, destructors run for all sub-objects. This includes both reuse by containment and reuse by inheritance.