Make virtual class use base class of another class - c++

I'm currently trying to build a hierarchy of classes that uses multiple inheritance. I have classes A, B, C and D, related as such:
struct A
{
int a;
A(int a_) : a(a_) {}
};
struct B : virtual A
{
int b;
B(int a_, int b_) : A(a_), b(b_) {}
};
struct C : virtual A
{
C(int a_) : A(a_) {}
};
struct D : B , C
{
D(int a_, int b_) : A(a_), B(a_, b_) , C(a_) {}
};
My problem is that D is passing arguments to B and C that aren't needed, and I'd like to find a better way of doing this.
The best way I found would be to have D initialize B first with B(a_, b_) and then B would initialize A with A(a_) and then I'd initialize C with C(a_) (even though the argument wouldn't matter) and 'link' C's instance of A to B's A instance.
What I mean by this is that the memory layout of D would look something like this:
Where C's base class A would be located inside of B.
I've tried multiple ways to do this in C++, but I haven't found one where it would let me change where C's A was located nor do I know if it possible.
My question is if this is possible to achieve in C++ using inheritance or possibly another tool?
Even if it possible, I would still have the problem of having to initialize both B and C with a 'dummy' argument that wouldn't do anything, this is fine for an int, but not for something more 'heavy'.
What I would want is for, when inheriting from B or C, you'd use an empty constructor to initialize it, and would use the other constructor when creating it normally, something like this:
struct A
{
int a;
A(int a_) : a(a_) {}
};
struct B : virtual A
{
int b;
B(int a_, int b_) : A(a_), b(b_) {}
protected:
// The A initialization will never get called
B(int b_) : A(0), b(b_) {}
};
struct C : virtual A
{
C(int a_) : A(a_) {}
protected:
// This initialization will never get called
C() : A(0) {}
};
struct D : B , C
{
D(int a_, int b_) : A(a_), B(b_) , C() {}
};
The only problem is that I'm not sure this has any side effects I should be aware of, or if it even works, I couldn't think of a way to test it well.
I'm not sure if asking two questions in one is acceptable, I believe that they make sense together, but if not, I'll edit it out and ask it as a different question.
Edit 1:
In relation to the duplicate, that answer states that the unused arguments will not be used to initialize the base-most class in the two derived classes.
My second question is how can I avoid having to provide the unused parameters to the two derived classes as the arguments can be very big and take a long time and memory copying. I have provided a possible solution, but I am not sure if this actually solves my problem and what side effects it can have.
Thus more concretely, my second question is Is this implementation of avoiding providing the parameters to the two derived classes good or are there any side effects I haven't taken in to account while building it?

Related

Variable and c++ inheritance [duplicate]

Why can't I do this?
class A
{
public:
int a, b;
};
class B : public A
{
B() : A(), a(0), b(0)
{
}
};
You can't initialize a and b in B because they are not members of B. They are members of A, therefore only A can initialize them. You can make them public, then do assignment in B, but that is not a recommended option since it would destroy encapsulation. Instead, create a constructor in A to allow B (or any subclass of A) to initialize them:
class A
{
protected:
A(int a, int b) : a(a), b(b) {} // Accessible to derived classes
// Change "protected" to "public" to allow others to instantiate A.
private:
int a, b; // Keep these variables private in A
};
class B : public A
{
public:
B() : A(0, 0) // Calls A's constructor, initializing a and b in A to 0.
{
}
};
Leaving aside the fact that they are private, since a and b are members of A, they are meant to be initialized by A's constructors, not by some other class's constructors (derived or not).
Try:
class A
{
int a, b;
protected: // or public:
A(int a, int b): a(a), b(b) {}
};
class B : public A
{
B() : A(0, 0) {}
};
Somehow, no one listed the simplest way:
class A
{
public:
int a, b;
};
class B : public A
{
B()
{
a = 0;
b = 0;
}
};
You can't access base members in the initializer list, but the constructor itself, just as any other member method, may access public and protected members of the base class.
# include<stdio.h>
# include<iostream>
# include<conio.h>
using namespace std;
class Base{
public:
Base(int i, float f, double d): i(i), f(f), d(d)
{
}
virtual void Show()=0;
protected:
int i;
float f;
double d;
};
class Derived: public Base{
public:
Derived(int i, float f, double d): Base( i, f, d)
{
}
void Show()
{
cout<< "int i = "<<i<<endl<<"float f = "<<f<<endl <<"double d = "<<d<<endl;
}
};
int main(){
Base * b = new Derived(10, 1.2, 3.89);
b->Show();
return 0;
}
It's a working example in case you want to initialize the Base class data members present in the Derived class object, whereas you want to push these values interfacing via Derived class constructor call.
Why can't you do it? Because the language doesn't allow you to initializa a base class' members in the derived class' initializer list.
How can you get this done? Like this:
class A
{
public:
A(int a, int b) : a_(a), b_(b) {};
int a_, b_;
};
class B : public A
{
public:
B() : A(0,0)
{
}
};
While this is usefull in rare cases (if that was not the case, the language would've allowed it directly), take a look at the Base from Member idiom. It's not a code free solution, you'd have to add an extra layer of inheritance, but it gets the job done. To avoid boilerplate code you could use boost's implementation
Aggregate classes, like A in your example(*), must have their members public, and have no user-defined constructors. They are intialized with initializer list, e.g. A a {0,0}; or in your case B() : A({0,0}){}. The members of base aggregate class cannot be individually initialized in the constructor of the derived class.
(*) To be precise, as it was correctly mentioned, original class A is not an aggregate due to private non-static members

multiple inheritance diamond problem without default constructor

I end up with a multiple inheritance diamond problem with the situation that there is no default constructor in the base class.
struct A {
A(int x) {}
};
struct B : virtual public A {
using A::A;
};
struct C : virtual public A {
using A::A;
};
struct D : virtual public B, public C {
D(int x) : B(x), C(x) {}
};
int main() {
D d(1);
}
The compiler is complaining that:
error: constructor for 'D' must explicitly initialize the base
class 'A' which does not have a default constructor
D(int x) : B(x), C(x) {}
But, I don't really have access to A from D... How can I fix that? Thanks
You do have an access to A since you're inheriting from it.
You would have to call the constructor of A in order to construct the object D.
Your D constructor should look something like that :
D(int x) : A(x), B(x), C(x) {}
Because of the multiple inheritance, constructors of B and C would ignore the A(x) part and you will have only one object which is D.

Inheritance in C++: define variables in parent-child classes

Problem
I am looking for the best way to define the variables in parent-child classes, in order to be called by a pointer to their parent class.
This is the protocode:
class Base {
public:
virtual void function() = 0;
};
class A : public Base {
public:
int a, b;
A(int a_, int b_) : a(a_), b(b_) {};
void function() { // do something.. }
};
class B : public Base {
public:
int a, b;
B(int a_, int b_) : a(a_), b(b_) {};
void function() { // do something.. }
};
Base* elements[2] = {
new A(1,2),
new B(3,4)
};
Since I define a, b in both constructors, I might define them in the abstract class Base. This way the code should be more efficient and clean. Is this practice correct? How should I define them?
Possible solutions
The solution I have in mind is implementing a function that returns for example a like this:
class Base {
public:
virtual int return_a() = 0;
};
class A : public Base {
public:
int a, b;
A(int a_, int b_) : a(a_), b(b_) {};
int return_a() {
return a;
}
};
int main(){
int a = elements[0]->return_a();
}
This works, but I am sure it is not an efficient way. Is it better to define a, b in the abstract class? Thanks
Is this practice correct?
I think this is turning into an opinion-based question-answer. If all your derived classes must include the members a and b, then in my opinion, they should be part of the base class. This way, you are guaranteed that all your derived classes will include the members a and b and you (or someone else) won't run the risk of forgetting to include them. Furthermore, by including the members in the base class, you save memory by not having to include them at every single derived class. C++'s virtual provides you with all the necessary tools to accomplish polymorphism, which is what happens when you create an array of Base *.
I would also recommend you use the keyword override for the virtual functions that are overridden in the derived class, and the keyword final for the derived classes that are not meant to become base classes. You can read the benefits of using those keywords from Scott Meyers Modern C++ book.
struct Base
{
int a, b;
Base(int a_, int b_) : a(a_) , b(b_)
{;}
virtual void function() = 0;
};
struct A : Base // A can be a base class of another class.
{
A(int a_, int b_) : Base(a_,b_)
{;}
void funtion() // this will compile, but it's not going to override the Base::function()
{;}
};
struct B final : Base // B can never become a base class.
{
B(int a_, int b_) : Base(a_,b_)
{;}
void funtion() override // this won't compile because override will see that we mis-spelled function()
{;}
};
However, there is no C++ rule that prohibits you from including the members in all of your derived classes.
Also, if all your members are public, then you can use a struct to avoid having to type public inside the classes and in the inheritance method.
struct Base
{
// all members are public
};
struct Derived : Base // public inheritance by default.
{
// all members are public
};
This is somewhat opinion based, but here is what I think based on the code you have posted.
Since you have made Base (from which all classes are derived) an abstract class, it appears that you want to use it as an interface.
In that case, it is better to distinguish between interface inheritance and implementation inheritance. Let Base not have any data which means that it would not require any constructors.
This is one of the coding guidelines given by Bjarne Stroustrup titled: When designing a class hierarchy, distinguish between implementation inheritance and interface inheritance.
The reason given is:
Implementation details in an interface make the interface brittle; that is, make its users vulnerable to having to recompile after changes in the implementation. Data in a base class increases the complexity of implementing the base and can lead to replication of code.
Note that you do not need getters or setters if the data in derived classes is public.

Confusion about initializers in C++ derived classes [duplicate]

Why can't I do this?
class A
{
public:
int a, b;
};
class B : public A
{
B() : A(), a(0), b(0)
{
}
};
You can't initialize a and b in B because they are not members of B. They are members of A, therefore only A can initialize them. You can make them public, then do assignment in B, but that is not a recommended option since it would destroy encapsulation. Instead, create a constructor in A to allow B (or any subclass of A) to initialize them:
class A
{
protected:
A(int a, int b) : a(a), b(b) {} // Accessible to derived classes
// Change "protected" to "public" to allow others to instantiate A.
private:
int a, b; // Keep these variables private in A
};
class B : public A
{
public:
B() : A(0, 0) // Calls A's constructor, initializing a and b in A to 0.
{
}
};
Leaving aside the fact that they are private, since a and b are members of A, they are meant to be initialized by A's constructors, not by some other class's constructors (derived or not).
Try:
class A
{
int a, b;
protected: // or public:
A(int a, int b): a(a), b(b) {}
};
class B : public A
{
B() : A(0, 0) {}
};
Somehow, no one listed the simplest way:
class A
{
public:
int a, b;
};
class B : public A
{
B()
{
a = 0;
b = 0;
}
};
You can't access base members in the initializer list, but the constructor itself, just as any other member method, may access public and protected members of the base class.
# include<stdio.h>
# include<iostream>
# include<conio.h>
using namespace std;
class Base{
public:
Base(int i, float f, double d): i(i), f(f), d(d)
{
}
virtual void Show()=0;
protected:
int i;
float f;
double d;
};
class Derived: public Base{
public:
Derived(int i, float f, double d): Base( i, f, d)
{
}
void Show()
{
cout<< "int i = "<<i<<endl<<"float f = "<<f<<endl <<"double d = "<<d<<endl;
}
};
int main(){
Base * b = new Derived(10, 1.2, 3.89);
b->Show();
return 0;
}
It's a working example in case you want to initialize the Base class data members present in the Derived class object, whereas you want to push these values interfacing via Derived class constructor call.
Why can't you do it? Because the language doesn't allow you to initializa a base class' members in the derived class' initializer list.
How can you get this done? Like this:
class A
{
public:
A(int a, int b) : a_(a), b_(b) {};
int a_, b_;
};
class B : public A
{
public:
B() : A(0,0)
{
}
};
While this is usefull in rare cases (if that was not the case, the language would've allowed it directly), take a look at the Base from Member idiom. It's not a code free solution, you'd have to add an extra layer of inheritance, but it gets the job done. To avoid boilerplate code you could use boost's implementation
Aggregate classes, like A in your example(*), must have their members public, and have no user-defined constructors. They are intialized with initializer list, e.g. A a {0,0}; or in your case B() : A({0,0}){}. The members of base aggregate class cannot be individually initialized in the constructor of the derived class.
(*) To be precise, as it was correctly mentioned, original class A is not an aggregate due to private non-static members

C++ delegating ctor and parent ctor with argument

This does not seem to work in C++11:
class B : public A
{
public:
B(const A& a)
: A(a) // parent constructor for passing the parameter
, B() // delegating constructor for init of other members
{};
// ...
};
gcc tells me that an initializer for a delegating constructor must appear alone.
How do I both call the constructor of the parent class with the parameter, and call the basic constructor of the B class? (I have a bunch of other constructors in B that need the same behavior).
Right now I am considering writing a private B::init() function and use it in all constructor bodies, but that tastes a bit much of C++03.
What is the preferred solution?
I believe the preferred way of delegation is the other way around, it's not meant to be used to refactor common parts of constructors, but rather to define the simpler one as a special case of a the more complex case.
So you should start with B(const A& a) and use this as delegation target.
class B : public A
{
public:
B() : B(A());
B(const A& a) : A(a) // parent constructor for passing the parameter
{};
};
You are calling A() anyways when creating B.
The rationale behind it is when you have two "partially specialized" c'tors, you wouldn't be able to use them to initialize the complex one. E.g:
class B : public A
{
public:
B() {};
B(int) : B() {};
B(double) : B() {};
B(double,int) : B(int), B(double) {}; // can't do it.
};
I believe the technical reason is explained in Bathsheba's answer. Look what would happen if you had a common part in B():
class B : public A
{
public:
B() {};
B(int) : B() {};
B(double) : B() {};
B(double,int) : B(int), B(double) {}; //ooops would get B() called twice!
};
It's the diamond problem known from inheritance. The solution is to reverse the logic.
class B : public A
{
public:
B() : B(0,0) {};
B(int a) : B(a,0) {};
B(double d) : B(0,d) {};
B(double a, int d) {/*full implementation*/};
};
B(const A& a) : A(a), B() does not make sense since B() will also initialise the base class A. That would essentially be a duplicate initialisation, which is an inherent contradiction.
The only real option for the language is to disallow anything else if you use a delegated constructor. That's what your compiler is telling you.