John Gilmore
John Gilmore

Reputation: 2815

Is it possible to call the constructor of a super class, two classes away from the current class in C++

I have three classes that inherit as follows:

Class_A  
Class_B : public Class_A  
Class_C : public Class_B

Class_A contains a constructor:

public: Class_A(const char *name, int kind);

Class_B does not contain that constructor.

In Class_C I wish to invoke the constructor of Class_A. Something like:
Class_C(const char *name, int kind) : Class_A::Class_A(name,kind) {}

The problem is that I cannot add an intermediate constructor to Class_B, because Class_B is generated code that regenerates every time I make clean. So I cannot make any lasting changes to Class_B. Needless to say, the above line of the constructor in Class_C gives the error: "type 'Class_A' is not a direct base of 'Class_C'".

Is there a way that I may invoke the constructor of Class_A in the subclass Class_C, without requiring the same type of constructor in Class_B?

Upvotes: 6

Views: 240

Answers (7)

James Kanze
James Kanze

Reputation: 153909

The only possibility would be for Class_B to inherit virtually from Class_A; in that case, the constructor for Class_A would be called from the most derived class. But since this would also involve changing the code generator, or the input to the code generator, you might as well change it to add the additional constructor to Class_B.

If you really can't change the code generator or its input in a way that would cause it to generate the additional constructor, but you can change Class_A and Class_C, then there are two possible solutions:

  • The simplest is simply to use deferred initialization; add a function to Class_A which takes the appropriate parameters, and does the initialization after the constructor has finished. While the simplest, this method can only be used if Class_A can be made to support default construction as well, and if all of the members of Class_A support assignment, with semantics such that default construction followed by assignment has the same result as construction using the arguments you give (i.e. no reference members, no noncopiable members, no const members, etc.).
  • Alternatively, you can put all of the functionality (or at least the data members) of Class_A in a separate class, and have Class_A derive from this class virtually. Class_C will then call the constructor of this new class.
  • Upvotes: 1

    Tony Delroy
    Tony Delroy

    Reputation: 106076

    You're probably stuffed (assuming you can't change class B's generation), but if there happen to be any constructors for B that are templates, then you can specialise one (for a unique dummy class) and create your own custom constructor with whatever base-class-A construction you feel like....

    Otherwise, while less efficient, the obvious, safe, clean approach is to use A's operator= (if any) to get the value you want into it...

    C c;
    c = A(x, y);
    

    If those fields of A can only be set at construction time, then it's a harder problem....

    (Stepping momentarily and deeply into the dreaded land of Undefined Behaviour, you could invoke A's destructor then placement new it, but A's lifespan is meant to span Bs and Cs. Practically, the obvious risk is that A created something like a heap-allocated value that B or C already holds a pointer to and may try to use after A destructor releases it....)

    Upvotes: 1

    edA-qa mort-ora-y
    edA-qa mort-ora-y

    Reputation: 31851

    If you can make the base class a virtual base class you can do this, as virtual bases must always uninitialized in the outermost constructor directly. If you can't change how ClassB inherits from A you can always do this:

    RealClassA
    ClassA : public virtual RealClassA
    ClassB : public ClassA
    ClassC : public ClassB
    

    Then in the ClassC constructor you can call RealClassA(...) directly.

    Usually this feature of virtual inheritence is a real pain, but it might actually help you here.

    Upvotes: 1

    iammilind
    iammilind

    Reputation: 69988

    By language one cannot access non-immediate base constructors.

    By the way, If Class_A constructor signature is,

    Class_A(const char *name, int kind);
    

    then, Class_B should have some constructor which will entertain the Class_A constructor of the given type. Otherwise it will give compiler error.

    The only way to get rid of that error is to provide default arguments to Class_A::Class_A(...).

    Upvotes: 0

    neuront
    neuront

    Reputation: 9612

    You could add a constructor for Class_B, which has the same signature as ctor of Class_A, and then let Class_C call that in ctor.

    Besides, a class ctor initialization list could not set values for members in base class (no matter they are private protected or public). You must use the base class' ctor to set them.

    Upvotes: 0

    Bruce
    Bruce

    Reputation: 7132

    If B is generated, then change the generator, otherwise you're doomed.

    Depending on what B is doing, might be a solution to change your design to have C derivation from A and B directly (if A is your container class and B a processing class). Doing this, might require you to template B. If B needs to act on A members, you can still give an A pointer to B constructor...

    Upvotes: 0

    Xeo
    Xeo

    Reputation: 131789

    If you can't change the code that generates B, then you are out of luck, AFAIK. But if the A class contains such a constructor, maybe you can get away with adding a simple member function that sets those two variables and call it from inside the C constructor? May not be as efficient as it gets, but atleast it works.

    Upvotes: 4

    Related Questions