neo-mashiro
neo-mashiro

Reputation: 534

Calling a protected constructor from a derived constructor

I have a base class with a protected constructor because this class should not be directly instantiated.

    class Transform {
      protected:
        Transform();
        ~Transform() {}

      public:
        glm::vec3 some_member;  // value will be initialized in the constructor
        ...  // many other members
    }

Then I have many derived classes

    class Camera : public Transform {
      public:
        Camera();
        ...
    }
    class Light: public Transform {
      public:
        Light();
        ...
    }

I'm surprised that the base class constructor is not called by default in the derived constructor, I thought it'd be called automatically, which turned out to be wrong. So now I want to call the base class constructor explicitly so the base class members will be correctly initialized, so I tried:

    Camera::Camera() {
        Transform::Transform();  // error C2248: cannot access protected member
        __super::Transform();  // error C2248: cannot access protected member
    }

The base constructor is protected, not private, why can't I access it in derived classes?

Magically, I found that this works fine, but I don't understand why, what's the difference here?

    Camera::Camera() : Transform() {  // compile successfully
        ...
    }

Upvotes: 0

Views: 1243

Answers (2)

Loki Astari
Loki Astari

Reputation: 264381

This is not doing what you expect:

Camera::Camera() {
    Transform::Transform();
    __super::Transform();
}

This constructor explained:

Camera::Camera()
   // Before you code is executed.
   // The base class and all the member variables are constructed.
   // So between this comment and the function body there is an
   // implied call to the base class constructor and then each
   // member constructor.

   : Transform()     // This is automatically generated by the
                     // compiler.

   // Add constructor for other members here.
   // or the compiler will generate them for you.
{

    // This does not do what you think it does.
    // Here you are directly calling the constructor of 
    // a temporary object. So you are attempting to
    // create another object here this is why you don't have
    // accesses because you only have accesses your base class 
    // members not the members of another object.
    Transform::Transform();


    // __super must be some implementation detail
    // it is not part of the language or something you have defined.

    // But you are calling its constructor (Assuming its Transform class)
    // Which is probably illegal if it is already constructed.
    
    __super::Transform();
}

Magically, I found that this works fine, but I don't understand why, what's the difference here?

Camera::Camera()
    // This section is called the initalizer list.
    : Transform()
    // This is where the constructors of base class and all
    // members go.

{  // compile successfully
    ...
}

Upvotes: 1

eerorika
eerorika

Reputation: 238311

I'm surprised that the base class constructor is not called by default in the derived constructor

If a base class has an applicable constructor, then it will be called. If there is no applicable constructor, then it must be called explicitly or the derived constructor will be ill-formed.

Some examples:

class Base1 {
protected:
    Base1() {}
};

struct Derived1 : Base1 {
    Derived1() {} // calls the base constructor
};

class Base2 {
protected:
    Base2(int) {}
};

struct Derived2a : Base2 {
    Derived2a() {} // ill-formed
};


struct Derived2b : Base2 {
    Derived2b() : Base2(42) {} // explicit call
};

The base constructor is protected, not private, why can't I access it in derived classes?

You can access the constructor. You simply cannot call any constructor within body of a function regardless of the access.

If you want to call a base constructor explicitly (which you don't need to do in case of the default constructor), then you must use the member initialiser list. Despite the name, that's where base sub objects are initialised along with the members sub objects.

Magically, I found that this works fine, but I don't understand why, what's the difference here?

The difference is that you're using the member initialiser list which is where the base is initialised. There is no magic involved.

Upvotes: 3

Related Questions