【问题标题】:Accessing a parent member in class constructor's initialization list访问类构造函数初始化列表中的父成员
【发布时间】:2019-09-03 19:06:34
【问题描述】:

假设下面的例子,SecondClass 继承自 FirstClass 并且它的构造函数应该和 FirstClass 的构造函数做同样的事情。我不想重复,所以我这样做:

SecondClass(int x)  : FirstClass(x) {...}

同时,我想填充name 成员,每个类应该有不同的值。在 FirstClass 中,我使用

FirstClass(int x) : BaseClass("first"), x(x) {...}

在SecondClass,我试过了

SecondClass(int x) : FirstClass(x), name("second")

编译失败(显然),因为 "class 'SecondClass' 没有任何名为 'name' 的字段"

那么,我怎样才能实现我想要的呢?我不想在每个类构造函数中将name 作为参数传递(这似乎是一种标准方法),因为它应该是固定的,而不是由调用者设置的。

完整示例代码:

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

class BaseClass {
protected:
    string name;
public:
    BaseClass(const string name) : name(name) {};

    virtual void doSomething() {};
};

class FirstClass : public BaseClass {
protected:
    int x;
public:
    FirstClass(int x) : BaseClass("first"), x(x) {
        cout << "FirstClass constructor, name=" << this->name << endl;
        // do something more with `x`
    };

    virtual void doSomething() {
        // do something with `x`
    }
};

class SecondClass : public FirstClass {
public:
    // SecondClass's constructor should do the same as FirstClass,
    // but I want this->name to contain "second"
    SecondClass(int x)  : FirstClass(x), name("second")
    {
        cout << "SecondClass constructor, name=" << this->name << endl;
    };

    virtual void doSomething() {
        // do something else with `x`
    };
};

int main()
{
    BaseClass *c1, *c2;

    c1 = new FirstClass(1);
    c2 = new SecondClass(1);

    c1->doSomething();
    c2->doSomething();
}

【问题讨论】:

  • “那么,我怎样才能实现我想要的?” - 将适当的构造函数添加到FirstClass,允许您传入特定的“名称”,然后使用来自SecondClass 的构造函数。 .?记住;一个类可以有多个构造函数。

标签: c++ inheritance constructor


【解决方案1】:

如果您在SecondClass 中同时继承BaseClassFirstClass,虚拟继承将“解决”问题。我不建议这样做,但是这样做,在实例化SecondClass时,FirstClass不会重新初始化BaseClass

#include <iostream>
#include <memory>
#include <string>

using std::cout, std::string;

class BaseClass {
protected:
    string name;

public:
    BaseClass(const string Name) : name(Name) {
        cout << "BaseClass, name=" << this->name << "\n";
    };
    virtual ~BaseClass() {}                               // <- you really DO need this
};

class FirstClass : public virtual BaseClass {
protected:
    int x;

public:
    FirstClass(int X) : BaseClass("first"), x(X) {
        cout << "FirstClass, name=" << this->name << "\n";
    };
};

class SecondClass : public virtual BaseClass, FirstClass {
public:
    SecondClass(int X) : BaseClass("second"), FirstClass(X) {
        cout << "SecondClass, name=" << this->name << "\n";
    };
};

int main() {
    // this is a better alternative than raw base class pointers:
    std::unique_ptr<BaseClass> c1, c2;

    cout << "--- FirstClass ---\n";
    c1 = std::make_unique<FirstClass>(1);
    cout << "--- SecondClass ---\n";
    c2 = std::make_unique<SecondClass>(2);
    cout << "---\n";
}

输出:

--- FirstClass ---
BaseClass, name=first
FirstClass, name=first
--- SecondClass ---
BaseClass, name=second
FirstClass, name=second
SecondClass, name=second
---

【讨论】:

  • 创意解决方案。我喜欢它。但我同意,这不是要走的路……虚拟继承有变得……复杂和棘手的趋势。
  • 我不确定我是否应该发布它。我从来没有写过这样的真实代码。 :)
【解决方案2】:

创建另一个构造函数

您可以像任何旧函数一样重载构造函数,因此重载FirstClass 的构造函数以获取字符串名称:

FirstClass(int x, string name) : BaseClass(name), x(x) {

但是任何人都可以使用不是"first" 的名称来创建FirstClass!为了解决这个问题,你可以将这个构造函数设为protected,这样只有孩子才能调用它:

protected:
    FirstClass(int x, string name) : BaseClass(name), x(x) {

然后你可以从你的 SecondClass 构造函数中调用它:

SecondClass(int x) : FirstClass(x, "second") {

【讨论】:

    【解决方案3】:

    解决问题的方法可能在于改变你对各个类职责的看法。

    1. 最好让非叶子类处于不可实例化状态。如果您遵循此准则,FirstClass 将无法实例化,因此构造函数

      FirstClass(int x) : BaseClass("first"), x(x) {...}
      

      没有意义。只有

      FirstClass(std::string name, int x) : BaseClass(name), x(x) {...}
      

      有道理。那么,SecondClass 很合适。

      SecondClass(int x)  : FirstClass("second", x) { ... }
      
    2. 如果“name”应该代表最派生的对象类型,最好使用virtual 成员函数来返回它,而不是基类中的成员变量。

      class BaseClass 
      {
         ...
         virtual std::string getName() const = 0;
         ...
      };
      
      class FirstClass : public BaseClass 
      {
         ...
         virtual std::string getName() const { return "first"; }
         ...
      };
      
      class SecondClass : public FirstClass 
      {
         ...
         virtual std::string getName() const { return "second"; }
         ...
      };
      

    【讨论】:

    • 请注意,如果您需要 Base 的构造函数中的名称,则多态解决方案将不起作用。它总是会调用抽象的BaseClass::getName()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 2011-05-02
    相关资源
    最近更新 更多