Як я можу ініціалізувати змінні елемента базового класу у похідному конструкторі класу?


123

Чому я не можу цього зробити?

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

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

};

7
Ви запитуєте, чому ви не можете цього зробити, що є питанням дизайну мови, або ви ставите питанням, як обійти це обмеження мови?
Роб Кеннеді

Я подумав, що існує якийсь особливий спосіб зробити це, про що я не знаю, без використання базового конструктора.
amrhassan

Члени базового класу вже ініціалізовані до моменту запуску конструктора похідного класу. Ви можете призначити їх, якщо у вас є доступ, або встановити для них виклики або ви можете надати значення для них конструктору базового класу, якщо такий є відповідним. Єдине, чого ви не можете зробити в розробленому класі - це ініціалізувати їх.
Маркіз Лорн

Відповіді:


143

Ви не можете форматувати aі bв 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.
    {
    } 
};

32
хоча ваш приклад правильний, ваше пояснення вводить в оману. Це не означає, що ви не можете форматувати a і bв B::B()тому , що вони є приватними. Ви не можете їх ініціалізувати, оскільки вони не є членами class B. Якщо ви зробили їх публічними чи захищеними, ви можете призначити їх у органі B::B().
R Самуїл Клачко

2
крім того, ви вирішення робить клас А не сукупним, що може бути важливим, тому його необхідно згадати.
Гена Бушуєва

1
@R Самуель Клачко: Добрий момент. Коли я писав відповідь, то спочатку набрав "Ви не можете отримати доступ aта b..." і змінив її на "Не можете ініціалізувати ...", не впевнившись, що решта речення мала сенс. Публікація відредагована.
У силіконі

1
@Gene Bushuyev: Клас у вихідному коді у питанні не є сукупним (є нестатичні приватні члени)
David Rodríguez - dribeas

@David - правильно, що є помилкою користувача, і я намагаюся дійти до намірів користувача, пропускаючи поверхнево.
Гена Бушуєва

26

Залишаючи в стороні той факт , що вони private, так aі bє членами A, вони покликані бути инициализирован Aконструкторами «S, що не конструкторами якого - то іншого класу (похідний чи ні).

Спробуйте:

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;
    }

};

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


1
Приємно. Чи є якісь недоліки в цьому способі?
Wander3r

2
@SaileshD: можливо, якщо ви ініціалізуєте об'єкт із дорогим конструктором. Спочатку він буде ініціалізований за замовчуванням, коли Bвиділяється екземпляр , а потім буде призначений всередині Bконструктора 's. Але я також думаю, що компілятор може все-таки це оптимізувати.
Фіолетова жирафа

1
Всередині class Aми не можемо покладатися на aі bініціалізації. class C : public AНаприклад, будь-яка реалізація , можливо, забуде дзвонити a=0;та залишати aнеініціалізованою.
Спаркофська

@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

Хоча це корисно у рідкісних випадках (якби це не було, мова дозволила б це прямо), подивіться на Базу з ідіоми учасника . Це не рішення для коду, вам доведеться додати додатковий рівень успадкування, але це робить роботу. Щоб уникнути кодового коду, ви можете скористатися реалізацією 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

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

(*) Якщо бути точним, як це було правильно сказано, оригінал class Aне є сукупністю через приватні нестатичні члени

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.