初始化基类成员的首选方法 (c++)

Posted

技术标签:

【中文标题】初始化基类成员的首选方法 (c++)【英文标题】:Preferred way to initialize base class members (c++) 【发布时间】:2018-05-14 07:47:52 【问题描述】:

我有一个基类:

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

还有一个派生类

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

基类是抽象的,所以只能创建派生类。 如何更好地实现派生的Ctor?

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

或者

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

将成员从 Base 构造函数中删除更好,因为它不能单独创建,还是保留在 Base 构造函数中以便为他保留分配?

【问题讨论】:

你心目中的“更好”是什么样的?无论如何,第一个违反准则是使用protected 数据成员。将其设为private,其余部分如下。 更多的是基于意见。我总是更喜欢遵循第一种方式,因为 1. 所有 Base 类逻辑都保留在 Base 类中; 2.如果m_aconst,这是唯一的方法 不要使用受保护的数据成员。 Stroustrup 说,在语言中使用它们是错误的。所有数据都应该是私有的。看到现在只有一种方式做事了吗?这甚至在我们开始谈论 const 和不可复制数据、移动语义以及所有那些奇怪而美妙的东西之前。 @n.m. - “所有数据都应该是私有的”是什么意思?如果我有一些需要使用受保护成员的派生方法 - 你会更喜欢这个成员的 getter 和 setter,而不是保护它吗? getter 和 setter 也会受到保护吗? “你更喜欢 getter 和 setter” 不,我更喜欢 重新设计。一个类不应该根据它拥有的内容来定义,而应该根据它做什么来定义。当你重新设计时,getter 和 setter 可以作为权宜之计。他们当然应该受到保护。 【参考方案1】:

您的第一个解决方案 - 为基类提供显式构造函数 - 作为一般模式更可取:

它避免了从 Base 继承的其他类忘记初始化 m_a。相反,类的签名表明需要初始化。

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

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

派生类可能有不止一个 CTor,还有更多可以忘记的地方

唯一的缺点:打字多一点——只要你不计算额外的“我今天有点懒,所以不要忘记在所有派生类构造函数中初始化 m_a”

如 cmets 中所述,“签名宣布要求”足以使 IMO 成为默认模式,“另一种方式要求使 m_a 受保护”也是如此。

【讨论】:

【参考方案2】:

我更喜欢:

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

通过这种方式,您可以使您的代码更加封装并且Base 成员关心它的初始化列表,在基类构造函数中可以有更多的初始化逻辑,这取决于m_a 而不是仅仅初始化m_a。在这种情况下,你将初始值传递给你的基类构造函数,然后他的构造函数中的派生类已经初始化了基类的构造函数。

您应该尝试将初始化值传递给您的 Base 类,假设您有 5 个 Derived 类,并且您需要在所有派生的 ctor 中初始化基类。

【讨论】:

以上是关于初始化基类成员的首选方法 (c++)的主要内容,如果未能解决你的问题,请参考以下文章

C++ - 在派生类中静态初始化基类受保护的成员变量

C++中的派生类,可以不定义对象直接调用基类的成员和调用自己的成员函数嘛???

c++继承总结

基类成员初始化

C++中如何在子类的构造函数中调用基类的构造函数来初始化基类成员变量

C++基类和派生类的构造函数