在类构造函数的初始化列表中访问父成员

时间:2019-09-03 19:06:34

标签: c++ inheritance constructor

假设下面的示例,其中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")

(显然)无法编译,因为“类'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();
}

3 个答案:

答案 0 :(得分:4)

创建另一个构造函数

您可以像重载任何旧函数一样重载构造函数,因此重载.see-all-categories的构造函数可以采用字符串名称:

FirstClass

但是,任何人都可以用非FirstClass(int x, string name) : BaseClass(name), x(x) { 的名字来制作FirstClass!要解决此问题,可以使此构造函数为"first",以便只有孩子可以调用它:

protected

然后您可以从protected: FirstClass(int x, string name) : BaseClass(name), x(x) { 构造函数中调用它:

SecondClass

答案 1 :(得分:4)

如果您同时继承BaseClass中的FirstClassSecondClass,则虚拟继承将“解决”该问题。我不建议这样做,但是这样做时,在实例化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 :(得分: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. 如果应该以“名称”表示派生最多的对象类型,则最好使用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"; }
       ...
    };