【问题标题】:Calling a protected constructor from a derived constructor从派生构造函数调用受保护的构造函数
【发布时间】:2021-10-05 14:57:17
【问题描述】:

我有一个带有受保护构造函数的基类,因为不应直接实例化此类。

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

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

那我有很多派生类

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

我很惊讶派生构造函数中默认不调用基类构造函数,我以为它会被自动调用,结果证明是错误的。所以现在我想显式调用基类构造函数,以便正确初始化基类成员,所以我尝试了:

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

基础构造函数是受保护的,不是私有的,为什么我不能在派生类中访问它?

神奇的是,我发现这很好用,但我不明白为什么,这里有什么区别?

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

【问题讨论】:

  • 这不是您从派生类的构造函数调用构造函数的方式,无论是否受保护。无需显式调用默认构造函数,但如果要显式调用默认构造函数,则需要在构造函数的初始化部分使用正确的语法。您的 C++ 教科书将包含更多信息和细节。
  • “惊讶的是,默认不调用基类构造函数” 被调用了。在这种情况下,您应该看不出有什么不同。 “我试过Transform::Transform(); 如果没有protected,那将创建一个临时对象,与Transform(); 相同。
  • @HolyBlackCat 无论访问限定符如何,它总是默认调用吗?我确实看到了差异,看起来值在其他地方发生了变化......
  • “不管访问限定符如何,它总是默认调用” 是的。
  • @SamVarshavchik 我刚刚意识到默认情况下确实调用了基本构造函数,我的错。顺便说一句,在什么情况下我应该在初始化部分调用基本构造函数?

标签: c++


【解决方案1】:

我很惊讶派生构造函数中默认不调用基类构造函数

如果一个基类有一个适用的构造函数,那么它将被调用。如果没有适用的构造函数,则必须显式调用它,否则派生的构造函数将是错误的。

一些例子:

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

基础构造函数是受保护的,不是私有的,为什么我不能在派生类中访问它?

您可以访问构造函数。无论访问如何,您都不能在函数体内调用任何构造函数。

如果您想显式调用基本构造函数(在默认构造函数的情况下不需要这样做),那么您必须使用成员初始化器列表。尽管有名字,但它是基础子对象与成员子对象一起初始化的地方。

神奇的是,我发现这很好用,但我不明白为什么,这里有什么区别?

不同之处在于您使用的是成员初始化程序列表,该列表是初始化基础的位置。没有魔法。

【讨论】:

    【解决方案2】:

    这不是你所期望的:

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

    这个构造函数解释:

    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();
    }
    

    神奇的是,我发现这很好用,但我不明白为什么,这里有什么区别?

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

    【讨论】:

      猜你喜欢
      • 2012-12-07
      • 2018-07-21
      • 2016-07-19
      • 2022-12-03
      • 2021-10-03
      • 1970-01-01
      • 2018-07-16
      • 2016-04-07
      • 2012-11-06
      相关资源
      最近更新 更多