0

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
        ...
    }
6
  • 3
    That's not how you call constructors, protected or not, from the derived class's constructor. There's no need to explicitly call default constructors, but if you want to explicitly call one you'll need to use the right syntax, in the constructor's initialization section. Your C++ textbook will have more information and details. Commented Jul 30, 2021 at 2:09
  • 5
    "surprised that the base class constructor is not called by default" It is called. You should be seeing no difference in this case. "I tried Transform::Transform();" That would create a temporary object, same as Transform();, if there was no protected. Commented Jul 30, 2021 at 2:09
  • @HolyBlackCat is it always called by default regardless of the access qualifier? I did see a difference tho, looks like the values are changed somewhere else... Commented Jul 30, 2021 at 2:21
  • 2
    "is it always called by default regardless of the access qualifier" Yep. Commented Jul 30, 2021 at 2:24
  • @SamVarshavchik I just realized that the base constructor is indeed called by default, my bad. Btw, in what scenario should I call base constructor in the initialization section? Commented Jul 30, 2021 at 2:28

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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
    ...
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.