C++:构造函数在继承链中的放置位置

C++: where to place constructor in inheritance chain?

本文关键字:置位 位置 构造函数 继承 C++      更新时间:2023-10-16

我有以下困境:

我有一个完整的抽象类。每个继承类将需要 3 个相同的参数。它们中的每一个都需要其他特定参数。

我可以:

1) 实现一个公共构造函数,用于在我的基类中初始化 3 个公共参数,但随后我必须为相应的字段制作非抽象的 getter(它们是私有的)。

2)让我的基类抽象并在继承的类中实现构造函数,但是我必须在每个类字段中为公共参数制作它。

哪种方法更好?我不想使用受保护的成员。

抽象类是至少有一个纯虚拟(或者,正如你所说的抽象)函数的类。拥有非抽象、非虚函数不会改变你的类是抽象的事实,只要它至少有一个纯虚函数。在基类中拥有通用功能,即使它是抽象的。

避免代码重复而又不污染数据成员的抽象接口的一种方法是引入额外的继承级别:

// Only pure virtual functions here
class Interface {
public:
    virtual void foo() = 0;
};
// Things shared between implementations
class AbstractBase : public Interface {
};
class ImplementationA : public AbstractBase {
};
class ImplementationB : public AbstractBase {
};

如果你的类看起来像这样,一个纯抽象类:

class IFoo {
   public:
    virtual void doThings() = 0;
}
class Foo {
   public:
      Foo(std::string str);
     void doThings() override;
}

您的继承的价值是为您提供在运行时用另一个替换 Foo 的机会,但将具体的实现隐藏在接口后面。你不能用构造函数来利用这个优势,没有虚拟构造函数这样的东西(这就是为什么存在抽象工厂模式这样的东西)。Foo的所有实现都采用std::string,而doThings的所有实现都使用该字符串?太好了,但这是巧合,不是合同,不属于IFoo.

让我们谈谈您是否在 IFoo 中创建了具体的实现,以便它是一个抽象类而不是一个纯粹的抽象类(顺便说一句,IFoo现在会是一个坏名字)。(*1)让我们假设使用继承来共享行为是你的类的正确选择,这有时是正确的。如果类具有需要初始化的字段,则创建一个protected构造函数(从每个子实现调用)并删除/省略默认构造函数。

class BaseFoo {
       private:
         std::string _fooo;
       protected:
         BaseFoo(std::string fooo) : _fooo(fooo) {}
       public:
         virtual void doThings() = 0;
         std::string whatsTheBaseString() { return _fooo;}
   }

以上是从子构造函数正确传递基类所需字段的方式。这是一个编译时保证,子类将"记住"正确初始化_fooo,并避免将实际成员fooo暴露给子类。尝试直接初始化所有子构造函数中的_fooo在这里是不正确的。

*1)很快,为什么?构图在这里可能是一个更好的工具?.