0

Until today I thought I understood inheritance much better than I apparently do. The goal here with this example was to design a framework of n number of classes (3 in this case), which should each exist uniquely for the life of the program. The forth class, whose implementation would contain the global main function would be responsible handling memory for the other classes. In addition, I was hoping to keep public members in the base classes protected, to prevent any other class from calling them.

Currently, the inheritance in the "Main" class is commented out as well as "protected:" keywords in each of the base classes. It doesn't do exactly what I want but everything behaves like normal. Constructors are called once (in ascending order), each function is called followed by the destructor.

My confusion here is actually two fold. If you un-comment the inheritance in the Main class, the code compiles, but new each ctor/dtor is called twice, first in ascending order order and then descending order. I've been unable to reason why this would occur, but it doesn't seem correct. Every explanation of inheritance I have ever seen is vague and doesn't explain why this would need to happen.

class Main //: public A, public B, public C

My second point of confusion is the protected members of the classes. I would think that if I un-comment the "protected:" keywords, proceeding the methods in the base classes, I should be able to call them from the inherited classes. As I understand it, I should even be able to inherit them as private, assuming I only want the children to have this functionality. Alas, I just get error messages about the method being protected.

I'm well aware that my understanding has some major shortfalls, but I've been exhaustively searching for an explanation with no luck. I could really use some constructive insight in to what is going on here,

Thanks,

#include <iostream>
#include <memory>
using namespace std;

class A
{
    public:
    A() { cout << "Ctor A\n";} 
    ~A() { cout << "Dtor A\n";} 

    //protected:
    void func() { cout << "Function A\n"; }
};

class B
{
    public:
    B() { cout << "Ctor B\n";}
    ~B() { cout << "Dtor B\n";} 

    //protected:
    void func() { cout << "Function B\n"; }
};

class C
{
    public:
    C() { cout << "Ctor C\n";} 
    ~C() { cout << "Dtor C\n";}

    //protected:
    void func() { cout << "Function C\n"; }
};

class Main //: public A, public B, public C
{
    public:
    Main(A *a, B *b, C *c);
    private:
    std::unique_ptr<A> mA;
    std::unique_ptr<B> mB;
    std::unique_ptr<C> mC;
};

Main::Main(A *a, B *b, C *c) : mA(a), mB(b), mC(c)
{
    mA->func();
    mB->func();
    mC->func();
}

int main()
{
    Main m(new A, new B, new C);
    return 0;
}

In case anyone is curious, I've been trying to compile this on ideone.com with the gcc v8.3 compiler.

mreff555
  • 1,049
  • 1
  • 11
  • 21
  • 2
    does this answer second part of your question https://stackoverflow.com/questions/16785069/why-cant-a-derived-class-call-protected-member-function-in-this-code? – Gaurav Dhiman Jan 02 '20 at 21:07
  • 1
    Also for first part if i understand your question correctly , yes it will call constructors twice if you uncomment public, because in your code `Main::Main(A *a, B *b, C *c)` call to constructors `A::A()`, `B::B()`, `C::C()` are implicit from derived class `Main`. – Gaurav Dhiman Jan 02 '20 at 21:15
  • It does. I suppose I understand the logic that an abstract class should refer to the member as it was inherited, however that really isn't all that intuitive. It seems like this should be better explained in documentation or tutorials. – mreff555 Jan 02 '20 at 21:15
  • 1
    You are confusing inheritance (*is-a* relationship) and composition (*has-a* relationship). If `Main` inherits from `A`, then you are expressing that `Main` *is an* `A`. If you give `Main` a member of type `A` (or `std::unique_ptr` to that), then you are expressing that `Main` *has an* `A`. Which one of the two you want depends on the semantics you want to give your classes, but you probably don't want *both*. Without more details it is hard to tell which you want. See also e.g. [this question](https://stackoverflow.com/questions/2218937/has-a-is-a-terminology-in-object-oriented-language). – walnut Jan 02 '20 at 21:39

1 Answers1

3

If you un-comment the inheritance in the Main class, the code compiles, but new each ctor/dtor is called twice, first in ascending order order and then descending order. I've been unable to reason why this would occur, but it doesn't seem correct. Every explanation of inheritance I have ever seen is vague and doesn't explain why this would need to happen.

You have three calls to new. Then you construct a Main, which requires constructing an A, a B, and a C. Since an instance of a Main is an A, a B, and a C, those three constructors have to be called to construct valid instances of those three types.

I would think that if I un-comment the "protected:" keywords, proceeding the methods in the base classes, I should be able to call them from the inherited classes.

Sure, but not on an arbitrary instance of the class that isn't even of the derived type! You have:

mA->func();

This is in a member function of class Main, but is operating on something that isn't an instance of class Main. The class Main has special access to itself as an instance of class A -- its internal interface can use protected functions of itself as an instance of class A, but that's it.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • So if the goal was to create 3 heap allocated objects, how would I manage this? A Are the implicit declarations stack allocated? – mreff555 Jan 02 '20 at 21:32
  • 2
    @mreff555 The way you've done it works, you did create three heap allocated objects when you called `new`. It's not clear exactly what you want to do, but you probably want your base classes to be just an interface with `Main` supporting all interfaces. – David Schwartz Jan 02 '20 at 21:53
  • You are correct. My concern is that these will be fairly large classes and I don’t want multiple objects for a single class unless needed. It seems that this would take away from the memory efficiency of the program. – mreff555 Jan 02 '20 at 23:09
  • @mreff555 Do you want to allocate three objects on the heap or not? If you want to allocate just one, there are ways to do that (forwarding constructors), but you pointed me in the opposite direction. – David Schwartz Jan 04 '20 at 00:59
  • Yes, that is what I want to do, I was just concerned with the multiple calls to the constructor. – mreff555 Jan 04 '20 at 15:11
  • 1
    @mreff555 If you use an interface class, its size will be effectively zero and its constructor will be optimized out. – David Schwartz Jan 04 '20 at 20:56