如何在派生类构造函数中初始化基类成员变量?


123

我为什么不能这样做?

class A
{
public:
    int a, b;
};

class B : public A
{
    B() : A(), a(0), b(0)
    {
    }

};

7
您是在问为什么不能这样做吗?这是一个语言设计问题,还是在问如何解决该语言限制?
罗伯·肯尼迪

我认为有某种我不知道的特殊方法,而不必使用基本构造函数。
amrhassan 2011年

在派生类构造函数开始运行时,基类成员已被初始化。您可以分配它们(如果有访问权限),或为它们调用设置方法,或者可以在合适的情况下为基类构造函数提供它们的值。您不能在设计的类中做的一件事就是初始化它们。
罗恩侯爵

Answers:


143

你不能初始化a,并bB因为它们不是成员B。它们是的成员A,因此只能对其A进行初始化。您可以将它们公开,然后在中进行分配B,但是不建议这样做,因为这会破坏封装。而是在中创建一个构造函数A以允许B(或的任何子类A)对其进行初始化:

class A 
{
protected:
    A(int a, int b) : a(a), b(b) {} // Accessible to derived classes
    // Change "protected" to "public" to allow others to instantiate A.
private:
    int a, b; // Keep these variables private in A
};

class B : public A 
{
public:
    B() : A(0, 0) // Calls A's constructor, initializing a and b in A to 0.
    {
    } 
};

32
虽然您的示例是正确的,但您的解释具有误导性。这并不是说你不能初始化 a,并bB::B()因为他们是私有的。您无法初始化它们,因为它们不是的成员class B。如果您将它们公开或受保护,则可以它们分配到的正文中B::B()
R Samuel Klatchko 2011年

2
另外,您的解决方案使A类成为非聚合类,这可能很重要,因此必须提及。
Gene Bushuyev 2011年

1
@R Samuel Klatchko:好点。当我写答案时,我最初键入“您无法访问ab...”,然后将其更改为“您无法初始化...”,但未确保其余句子的合理性。帖子已编辑。
in silico 2011年

1
@Gene Bushuyev:在问题的原代码的类是不是(也有非静态私有成员)
dribeas大卫-罗德里格斯

@David-正确,这是用户的错误,我正在尝试达到用户的意图,跳过肤浅的内容。
Gene Bushuyev 2011年

26

撇开事实,他们是private,因为ab是的成员A,他们是为了通过初始化A的构造函数,而不是由一些其他类的构造函数(衍生与否)。

尝试:

class A
{
    int a, b;

protected: // or public:
    A(int a, int b): a(a), b(b) {}
};

class B : public A
{
    B() : A(0, 0) {}
};

7

不知何故,没有人列出最简单的方法:

class A
{
public:
    int a, b;
};

class B : public A
{
    B()
    {
        a = 0;
        b = 0;
    }

};

你不能在初始化列表中访问基地的成员,但构造函数本身,就像任何其他成员方法,可以访问publicprotected基类的成员。


1
真好 这样有什么弊端吗?
Wander3r

2
@SaileshD:如果要使用昂贵的构造函数初始化对象,则可能会有。B分配实例时,它将首先进行默认初始化,然后在B的构造函数中进行分配。但是我也认为编译器仍然可以对此进行优化。
紫罗兰色长颈鹿

1
在里面class A我们不能依赖ab被初始化。任何实现class C : public A,例如,可能会忘记打电话a=0;,并留下a初始化。
Sparkofska '19

@Sparkofska,非常真实。最好在声明字段(class A { int a = 0;};)时或在基类的构造函数中默认初始化字段。子类仍然可以根据需要在其构造函数中重新初始化它们。
紫罗兰色长颈鹿

1
@ Wander3r另一个缺点是,并非所有类都具有赋值运算符。有些只能构造,而不能分配给它们。那您就完了...
马丁·佩卡

2
# include<stdio.h>
# include<iostream>
# include<conio.h>

using namespace std;

class Base{
    public:
        Base(int i, float f, double d): i(i), f(f), d(d)
        {
        }
    virtual void Show()=0;
    protected:
        int i;
        float f;
        double d;
};


class Derived: public Base{
    public:
        Derived(int i, float f, double d): Base( i, f, d)
        {
        }
        void Show()
        {
            cout<< "int i = "<<i<<endl<<"float f = "<<f<<endl <<"double d = "<<d<<endl;
        }
};

int main(){
    Base * b = new Derived(10, 1.2, 3.89);
    b->Show();
    return 0;
}

如果您要初始化派生类对象中存在的基类数据成员,而您想通过派生类构造函数调用来推送这些值,则这是一个工作示例。


1

虽然这在极少数情况下很有用(如果不是这种情况,则该语言会直接允许使用),但请查看Member idiom中的Base。这不是一个无代码的解决方案,您必须添加一个额外的继承层,但是它可以完成工作。为了避免样板代码,您可以使用boost的实现


0

你为什么不能这样做?因为该语言不允许您在派生类的初始化程序列表中初始化基类的成员。

你怎么能做到这一点?像这样:

class A
{
public:
    A(int a, int b) : a_(a), b_(b) {};
    int a_, b_;
};

class B : public A
{
public:
    B() : A(0,0) 
    {
    }
};

-1

如果您没有为类成员指定可见性,则默认为“私有”。如果要在子类中访问成员,则应将其设为私有或受保护。


-1

聚合类(如您的example(*)中的A)必须将其成员公开,并且没有用户定义的构造函数。它们通过初始化器列表初始化,例如A a {0,0};或在您的情况下B() : A({0,0}){}。基本集合类的成员不能在派生类的构造函数中单独初始化。

(*)确切地说,正如正确提到的那样,class A由于私人非静态成员的缘故,原件并非汇总

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.