Как я могу инициализировать переменные-члены базового класса в конструкторе производного класса?

123

Почему я не могу этого сделать?

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

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

};
amrhassan
источник
7
Вы спрашиваете, почему вы не можете этого сделать, что является вопросом языкового дизайна, или вы спрашиваете, как обойти это языковое ограничение?
Роб Кеннеди,
Я думал, что есть какой-то особый способ сделать это, о котором я не знаю, без использования базового конструктора.
amrhassan
Члены базового класса уже инициализированы к моменту запуска конструктора производного класса. Вы можете назначить их, если у вас есть доступ, или вызвать для них сеттеры, или вы можете передать значения для них конструктору базового класса, если он подходит. Единственное, что вы не можете сделать в разработанном классе, - это их инициализировать.
Маркиз Лорн

Ответы:

143

Вы не можете инициализировать aи bin, Bпотому что они не являются членами 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.
    {
    } 
};
In silico
источник
32
Хотя ваш пример верен, ваше объяснение вводит в заблуждение. Это не значит, что вы не можете инициализировать a и bв B::B()потому , что они являются частными. Вы не можете инициализировать их, потому что они не являются членами class B. Если вы сделали их общедоступными или защищенными, вы можете назначить их в теле B::B().
R Samuel Klatchko
2
Кроме того, ваше решение делает класс A неагрегированным, что может быть важно, поэтому об этом следует упомянуть.
Гена Бушуев
1
@R Сэмюэл Клатчко: Хорошее замечание. Когда я писал ответ, я сначала набрал «Вы не можете получить доступ aи b...» и изменил его на «Вы не можете инициализировать ...», не убедившись, что остальная часть предложения имеет смысл. Сообщение отредактировано.
In silico
1
@Gene Bushuyev: Класс в исходном коде в вопросе не является агрегатом (есть нестатические частные члены)
Дэвид Родригес - dribeas
@David - правильно, это ошибка пользователя, и я пытаюсь понять намерения пользователя, пропуская поверхностные.
Гена Бушуев
26

Оставляя в стороне тот факт, что они есть private, поскольку aи bявляются членами 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) {}
};
NPE
источник
7

Почему-то никто не перечислил самый простой способ:

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

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

};

Вы не можете получить доступ к базовым членам в списке инициализаторов, но сам конструктор, как и любой другой метод-член, может обращаться publicи к protectedчленам базового класса.

Фиолетовый жираф
источник
1
Ницца. Есть ли в этом недостатки?
Wander3r
2
@SaileshD: может быть, если вы инициализируете объект дорогостоящим конструктором. Сначала он будет инициализирован по умолчанию при Bвыделении экземпляра , затем он будет назначен внутри Bконструктора. Но я также думаю, что компилятор все еще может это оптимизировать.
Violet Giraffe
1
Внутри class Aмы не можем полагаться aи bна инициализацию. class C : public AНапример, любая реализация может забыть позвонить a=0;и оставить aнеинициализированным.
Sparkofska
@Sparkofska, очень верно. Лучше всего инициализировать поля по умолчанию либо на месте при их объявлении ( class A { int a = 0;};), либо в конструкторе базового класса. Подклассы по-прежнему могут повторно инициализировать их в своем конструкторе по мере необходимости.
Violet Giraffe
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;
}

Это рабочий пример на случай, если вы хотите инициализировать члены данных базового класса, присутствующие в объекте производного класса, в то время как вы хотите передать эти значения, взаимодействуя через вызов конструктора производного класса.

Manish Srivastava
источник
1

Хотя это полезно в редких случаях (если бы это было не так, язык позволил бы это напрямую), взгляните на идиому Base from Member . Это не решение без кода, вам придется добавить дополнительный уровень наследования, но оно выполняет свою работу. Чтобы избежать шаблонного кода, вы можете использовать реализацию 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

Если вы не укажете видимость для члена класса, по умолчанию она будет закрытой. Вы должны сделать своих участников закрытыми или защищенными, если хотите получить к ним доступ в подклассе.

TotoroTotoro
источник
-1

Совокупные классы, такие как A в вашем примере (*), должны иметь свои члены общедоступные и не иметь определяемых пользователем конструкторов. Они инициализируются списком инициализаторов, например, A a {0,0};или в вашем случае B() : A({0,0}){}. Члены базового агрегатного класса не могут быть индивидуально инициализированы в конструкторе производного класса.

(*) Если быть точным, как было правильно сказано, оригинал class Aне является агрегатом из-за частных нестатических членов

Гена Бушуев
источник