Наследование, вызов базовых классов ctors - PullRequest
1 голос
/ 22 июля 2010
class Base{
    public:
      Base(int val):_id(val){};
      int _id;
};

class Derived : Base {
    public:
      Derived(int val):Base(_id+val){};
};

int main(){
     Derived d(60);
}

почему это не дает ошибку? Базовый класс еще не создан, но я могу использовать _id?

спасибо

Ответы [ 6 ]

2 голосов
/ 22 июля 2010

Более простой пример: int x = x + 1; показывает, что компиляторы c ++ не отслеживают инициализацию переменных. В вашем примере _id существует в памяти (он имеет адрес), но никогда не был инициализирован. Однако, поскольку компилятор не отслеживает, ошибки нет.

1 голос
/ 22 июля 2010

Он не создан, но его память выделена, и поэтому _id существует и содержит неинициализированное значение.

0 голосов
/ 22 июля 2010

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

РЕДАКТИРОВАТЬ: причина _id является пригодной для использования, потому что память для него была выделена, она просто еще не была инициализирована. Подумайте что-нибудь вроде:

int x;
int y = x;

Вы не представляете, что будет в y, потому что x никогда не инициализировался.

0 голосов
/ 22 июля 2010

Представьте, что в точке, где вы ввели :, был невидимый malloc, чтобы запустить список инициализатора.Это даст вам неинициализированный блок памяти, достаточно большой, чтобы вместить объект Derived, включая часть Base.Компилятору известно смещение, на которое ссылается _id, поэтому он охотно дает вам ненужное значение, которое там находится.

Допустимо ссылаться на _id после вызова Base() в списке инициализатора.Компиляторы не делают особого случая, когда конструктор базового класса еще не был вызван.

0 голосов
/ 22 июля 2010

Базовый класс еще не создан, но я могу использовать _id?

Нет, вы не используете _id, потому что нет объекта_id пока.Но есть необработанная память объекта, и вы можете получить к ней доступ, используя идентификатор, интерпретируя необработанную память как объект int.

Выполнение вызывает Неопределенное поведение , и вам лучше этого не делать.Неопределенное поведение может привести к тому, что ваш HD будет отформатирован, вы забеременеете или программа, похоже, будет работать нормально и делать то, что должна.И вы никогда не знаете, какой это, так как он может отличаться для каждого компилятора, версии компилятора, фазы луны или чего-то еще.

В вышеприведенном случае, интерпретируя необработанную память как объект int, общий результат состоит в том, что любая битовая комбинация, существующая в этой позиции, интерпретируется как целое число, и результат используется.Однако платформы также могут перехватывать доступ к неинициализированной памяти и выдавать аппаратное исключение.
Если _id не является POD (std::string), вероятным, но не гарантированным результатом будет нарушение доступа.

Изменить в ответ на комментарий:

Вы можете получить доступ к элементам базового класса, даже неинициализированным, в списке инициализации производного класса простоштраф:

#include <iostream>

class Base{
public:
    Base(int val):id_(val){};
protected:
    int id_;
};

class Derived : Base {
public:
    Derived(int val):Base(id_+val), blah_(id_) {};
    int blah() const {return blah_;}
private:
    int blah_;
};

int main(){
    Derived d(60);
    std::cout << d.blah() << '\n';
    return 0;
}
0 голосов
/ 22 июля 2010

Я не могу понять вопрос. Это работает, потому что стандарт c ++ допускает такое поведение. Память для класса уже выделена в данный момент, поэтому можно использовать эту переменную.

...