C++中初始化基类成员的首选方法

3

I have a base class:

class Base {
protected:
    int m_a;
    virtual void foo() = 0;
}

还有一个或多个派生类

class Derived : public Base {
public:
    Derived(int a);
}

这个基类是抽象的,所以只能创建派生类。 如何更好地实现派生构造函数?

Derived::Derived(int a) : Base(a) {}
Base::Base(int a) : m_a(a) {}

或者

Derived::Derived(int a) { m_a = a;}
Base::Base(){}

在 Base 构造函数中是否更好移除成员,因为它不能单独创建,还是保留在 Base 构造函数中为其分配?


2
您心目中的“更好”是什么呢?无论如何,第一个违反的指南是使用protected 数据成员。将其改为private,其他的就会随之而来。 - Ulrich Eckhardt
3
这更多是基于个人意见的。我总是更喜欢遵循第一种方法,因为1. 所有“Base”类逻辑都保留在“Base”类中;2. 如果“m_a”是“const”,这是唯一的方法。 - Andrei R.
不要使用受保护的数据成员。Stroustrup表示,将它们包含在语言中是一个错误。所有数据都应该是私有的。现在只有一种方法可以做到这一点,更不用说常量、不可复制的数据、移动语义和其他奇怪而美妙的东西了。 - n. m.
“你更喜欢使用getter和setter函数吗?” “不,我相当倾向于进行重新设计。一个类的定义不应该基于它具有什么,而是基于它做些什么。Getter和setter函数可以作为一种权宜之计,同时在重新设计时也应该被保护。” - n. m.
@n.m. - 我想询问一个一般情况,即派生类需要使用/更改基类成员时应该怎么做。你会怎么做呢?
  1. 在每个派生类中声明此成员?2. 使用getter和setter?3. 将其设为私有?4. 为您需要访问的目的创建特殊方法?
- binyamina
显示剩余5条评论
2个回答

4
您的第一个解决方案——给基类一个明确的构造函数——是一种更好的通用模式:
  • 它避免了其他从Base继承的类忘记初始化m_a。相反,类的签名表明需要初始化。

  • 如果多个类从基类继承,并且初始化更加复杂(例如范围检查),则该代码和策略不会分布在多个派生类中。

  • 如果m_a是不可变的,则需要构造函数初始化。

  • 派生类可能有多个构造函数,更容易忘记初始化。

唯一的缺点:需要输入更多 - 只要您不计算附加的“我今天有点懒,所以不要忘记在所有派生类构造函数中初始化m_a”。

“签名宣布要求”的方式在我看来已足够使其成为默认模式,就像评论中提到的“另一种方法需要将m_a设置为protected”一样。


1

我更喜欢:

Derived::Derived(int a) : Base(a) {}
Base::Base(int a) : m_a(a) {}

通过这种方式,您可以使代码更加封装,Base 成员负责其初始化列表,基类构造函数中可能有更多的初始化逻辑,取决于 m_a 而不仅仅是对其进行初始化。在这种情况下,您将初始值传递给基类构造函数,然后派生类在其构造函数中初始化了基类的构造函数。
您应该尝试将初始值传递给您的 Base 类,想象一下您有 5 个 Derived 类,并且您需要在所有派生的构造函数中初始化基类。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接