【问题标题】:Is it possible to defer member variable initialization to inherited class without modifying the parent class?是否可以在不修改父类的情况下将成员变量初始化推迟到继承类?
【发布时间】:2017-08-09 19:06:47
【问题描述】:

我遇到了一个特定问题,我已将其转换为以下Minimal, Complete, and Verifiable example

#include <iostream>

class Foo {
    public:
        Foo(int v) : val(v) {}

        int get_val() const
        {
            return val;
        }

    private:
        int val;
};

class Parent {
    public:
        Parent() : member(0) {}

        const Foo& get_member() const
        {
            return member;
        }

    protected:
        Foo member;
};

// Nothing above this line should be changed

class Child : public Parent
{
    public:
        // This doesn't work (compile error)
        //Child() Parent::member(1) {}

        // Nor does this (also a compile error)
        //Child() this->member(1) {}
};

int main()
{
    Child x;
    std::cout << x.get_member().get_val() << std::endl;
    return 0;
}

此示例演示了我在一个较大的软件项目中遇到的问题,我从外部库继承但需要直接初始化父级的成员变量之一

不幸的是,父类没有参数化其成员初始化的构造函数。

如果Parent 类有一个表单的构造函数

Parent(int val) : member(val) {}

然后我可以写一个Child 构造函数为

Child() Parent::Parent(1) {}

但对我来说不是这样。

问题:是否可以将父级成员变量的初始化推迟到继承的类?如果有,怎么做?

【问题讨论】:

  • 没有。基类必须在派生类开始构建之前完全构建。不能只在派生类构造函数体中设置成员吗?
  • @NathanOliver 谢谢,不,我不能。父类使用设置一些网络连接的默认参数化初始化其成员。这些参数在初始化后无法更改。
  • 如果没有办法设置基类成员,你希望如何设置它?
  • @NathanOliver 我什么都不期待。我在问子类是否有办法覆盖父类的默认初始化。
  • 啊,很遗憾,没有。您不必为基类创建新的构造函数。

标签: c++ class inheritance initialization


【解决方案1】:

是否可以将父级成员变量的初始化推迟到继承的类?如果有,怎么做?

父类的成员变量要么在其成员初始化列表中初始化,要么在构造函数的主体中初始化。子类无法在其初始化列表中初始化父类的成员变量——这是语言所不允许的。看来,您能做的最好的事情就是在子类的构造函数的主体中设置父类成员的值。

例子:

struct foo
{
   int a;
};

struct bar : foo
{
   bar() : a(0) {} // Not allowed
};

但是

struct bar : foo
{
   bar() { a = 0; } // Allowed
};

【讨论】:

    【解决方案2】:

    最简单的方法是在Child类构造函数主体中初始化Parent::member(再次):

    class Child : public Parent
    {
        public:
            Child() {
                Parent::member = 1;
            }
    
    };
    

    请参阅live demo


    正如在 cmets 中阐明的那样,您不应该分配 Parent::member 变量。

    在这种情况下(让我们假设设计至少是有用的), 您通常可以为父类成员实例的某些属性应用 setter:

    class Foo {
        Foo(const& Foo) = delete;
        Foo& operator=(const& Foo) = delete;
    public:
        Foo() = default;
        int property() const;
        void property(int newVal);
    };
    
    class Parent {
    protected:
        Foo member;
    public:
        Parent() = default;
    };
    
    class Child : public Parent {
    public:
        Child() {
            Parent::member.property(1);
        };
    };
    

    【讨论】:

    • 它可能太窄了,我可以扩展它,但问题是关于覆盖父级的初始化程序。在这个答案中,父级仍然将Foo 初始化为0,然后子级创建一个Foo 的新实例,其值为1,并将其复制到member。实际上,我对此运行感到有些惊讶,因为我认为 = 形式的构造函数初始化仅在初始化期间有效。
    • 这是一个赋值,而不是初始化。它“有效”是因为 Foo 有一个转换构造函数。
    • 为什么不应该分配给Parent::member 变量?如果他们不希望将其分配给他们,他们会设置为 private
    • @DanielH 请询问 OP,这显然不是重点。顺便说一句,这就是有多少 组件框架 解决了问题。
    猜你喜欢
    • 2022-01-15
    • 2021-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-29
    • 1970-01-01
    • 2019-05-09
    • 1970-01-01
    相关资源
    最近更新 更多