Доступ к защищенному члену базового класса из производного класса

#c #class #protected #derived-class

#c #класс #защищенный #производный класс

Вопрос:

У меня есть следующий фрагмент кода:

 const int DATE_LENGTH = 6;

class BaseClass {
    protected:
        int date[DATE_LENGTH];
        int year;
    public:
        BaseClass(){}
        BaseClass(int *d) {
            for (int i = 0; i < DATE_LENGTH; i  ) { date[i] = d[i];}
            year = 1900   date[4] * 10   date[5];
        }
        void printYear() {
            cout << year << endl;
        }
};

class DerivedClass : public BaseClass {
    public:
        DerivedClass() {}
        void printYear() {
            cout << year << endl;
        }
};

int main(int argc, char *argv[]) {
    int dob[] = {1, 6, 1, 0, 9, 0};
    BaseClass base(dob);
    base.printYear(); // prints 1990

    DerivedClass derived;
    derived.printYear(); // prints 1439156608
}
  

У меня возникли проблемы с пониманием того, почему выходные данные из printYear() моего производного класса выводят мусор. Я упускаю что-то очень очевидное?

Будем признательны за любую помощь!

Комментарии:

1. Ваши конструкторы по умолчанию ничего не инициализируют, поэтому вы получаете все, что оказывается в памяти.

2. @T.C.: Это не совсем так, базовый подобъект действительно был инициализирован. Язык требует, чтобы это происходило. Просто была выбрана очень плохая инициализация.

3. @RetiredNinja: На самом деле это не то, что вы можете гарантировать. Это неопределенное поведение, и вы можете получить что-то из памяти, или пиццу, или потерять работу.

4. Вы можете получить пиццу из-за неопределенного поведения? Полный вперед!

5. @KerrekSB Ты прав, я требую свою пиццу! 🙂

Ответ №1:

Ваша программа имеет неопределенное поведение. Конструктор по умолчанию DerivedClass , который вы используете, не инициализирует year член.

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

 DerivedClass() { year = 1999; }
  

Комментарии:

1. Вы абсолютно уверены, что это UB и не может быть просто неопределенными значениями?

2. @Deduplicator, да, year неинициализирован, и для него выполняется преобразование lvalue в rvalue.

Ответ №2:

Конструктор класса по умолчанию BaseClass

 BaseClass(){}
  

не инициализирует элементы данных date и year

Этот конструктор по умолчанию вызывается конструктором класса по умолчанию DerivedClass при создании производного объекта

 DerivedClass derived;
  

Таким образом, эти элементы данных имеют произвольные значения, и ваша программа имеет неопределенное поведение.

Измените производный класс следующим образом

 class DerivedClass : public BaseClass {
    public:
        using BaseClass::BaseClass;
        DerivedClass() {}
        void printYear() {
            cout << year << endl;
        }
};
  

и создать объект, производный от

 DerivedClass derived( dob );
  

Или вместо объявления using вы можете сами явно определить конструктор в классе DerivedClass, который имеет один параметр типа int * и вызывает соответствующий конструктор базового класса. Например

 class DerivedClass : public BaseClass {
    public:
        DerivedClass() {}
        DerivedClass( int *d ) : BaseClass( d ) {}
        void printYear() {
            cout << year << endl;
        }
};
  

Ответ №3:

Другие предоставленные ответы касаются только части проблемы, отчасти потому, что сама дата никогда не определена в конструкторе по умолчанию DerivedClass ни одного из предоставленных в данный момент, а другая часть, являющаяся конструктором по умолчанию базового класса, все еще не имеет определенного значения ни для одной переменной класса (date или year). Если используется следующий код, в зависимости от того, какими должны быть дата и год по умолчанию, никаких дополнительных изменений для производного класса фактически не требуется.

 #include <iostream>

using namespace std;

const int DATE_LENGTH = 6;

class BaseClass {
    protected:
        int date[DATE_LENGTH];
        int year;
    public:
        BaseClass()
        {
            int date[] = {1, 6, 1, 0, 9, 0};
            year = 1900   date[4] * 10   date[5];
        }
        BaseClass(int *d)
        {
            for (int i = 0; i < DATE_LENGTH; i  ) { date[i] = d[i];}
            year = 1900   date[4] * 10   date[5];
        }
        void printYear() {
            cout << year << endl;
        }
};

class DerivedClass : public BaseClass {
    public:
        DerivedClass() {}
        void printYear() {
            cout << year << endl;
        }
};

int main(int argc, char *argv[])
{
    int dob[] = {1, 6, 1, 0, 9, 0};
    BaseClass base(dob);
    base.printYear(); // prints 1990

    DerivedClass derived;
    derived.printYear(); // prints 1439156608
    return 0;
}
  

Вывод приложения

 1990
1990