2015-07-08 5 views
1

У меня есть иерархия классов, где в производном классе объявлен объект класса Dog. Затем этот объект предоставляется в базовый класс в списке инициализации.Почему неконструированный объект может быть передан конструктору базового класса в производный список инициализации

Поскольку объект класса Dog не сконструирован до того, как вызывается конструктор базового класса, так что объект не должен использоваться. Но я вижу, что этот объект может быть использован в следующем коде:

#include <iostream> 
using namespace std; 

class Dog { 
    public: 
     Dog() {cout << "\n In Dog";} 
     void dfoo(){cout << "\n In dfoo";}; 
}; 

class Foo { 
    public: 
     Foo(Dog d){cout << "\n In Foo"; 
     d.dfoo(); 
    } 
}; 

class Bar : public Foo { 
    Dog d; 
    public: 
     Bar() : Foo(d) { 
      cout << "\n In Bar"; 
     } 
}; 

int main() { 
    cout << "\nHello"; 
    Bar b; 

} 

Выход:

Hello In Foo In dfoo In Dog In Bar Выходные данные показывают, что dfoo() была вызвана еще до объекта Dog был построен. Как это работает так? Не следует ли «d» быть мусором, поскольку это не инициализируется до вызова конструктора базы?

ответ

2

На практике ваш объект d содержит мусор.

Когда вы передаете d (по значению), вы неявно вызываете конструктор копирования для Dog и создаете новый объект типа Dog, выполнив конструктор копирования на мусорном объекте типа Dog.

Итак, ваш вопрос действительно: почему неявный конструктор копирования Dog не заботится о том, чтобы исходный объект никогда не строился.

Поскольку у объекта нет даже содержимого, для компилятора было бы очень много потрачено впустую, чтобы конструктор копирования ничего не заботился о его источнике.

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

Рассмотрим эту версию Dog:

class Dog { 
    public: 
     Dog() {cout << "\n In Dog";} 
     Dog(Dog const& source) { cout << "\n Trying to copy a Dog\n"; source.dfoo();} 
     virtual void dfoo() const {cout << "\n In dfoo";}; 
}; 

Теперь ваше использование этого происходит сбой, как вы могли бы ожидать (в результате источника конструктора копии, что никогда не был построен сам).

Ваш вызов конструктору копирования Dog скрыт внутри действия передачи по значению конструктору Foo. Обратите внимание, что это происходит до того, как конструктор Foo фактически достигнут. Это копирование Dog (со всеми его преимуществами и/или проблемами) происходит с явным определением конструктора копирования для Dog или без него. Если вы не определяете его, компилятор определяет его для вас.

+0

Как конструктор Foo может принять объект мусора, если он требует правильной собаки? – DKR

+0

Ошибка, когда программист сделал что-то неправильно, никогда не был главной целью проекта C++. Предполагается, что программы на C++ работают достаточно эффективно при правильной кодировке. Это ожидание эффективности устраняет большинство возможностей здравомыслия, проверяя действия программиста. Конструктор Foo не требует «правильной собаки». Это требует только того, что вы утверждаете, является Собакой. Тогда Dog :: dfoo() также требует только то, что вы утверждали как Собака (это фактически не использует этот объект). – JSF

+0

Добавление виртуальной функции в класс Dog и вызов этого объекта d из Foo имеет тот же эффект. Нет ошибки. – DKR

0

Если вы скомпилируете свой код в clang, вы получите предупреждение, но вы получите тот же результат.

clang++ -std=c++11 -Wall -pedantic -pthread main.cpp && ./a.out 

main.cpp: 21: 21: предупреждение: поле 'd' не инициализируется при использовании здесь [-Wuninitialized] Bar(): Foo (d) {

Так что это происходит?

Вы передаете неинициализированный объект Dog как параметр в конструктор Foo. Поскольку ваш конструктор принимает параметр Dog как значение, он попытается вызвать конструктор copy класса Dog для создания объекта Dog, даже если он не инициализирован. И системе удалось создать локальный объект Dog, используя конструктор копирования внутри конструктора Foo и вызывается функция dfoo().

+0

Да, я ожидал такого сообщения с g ++. Рад, что кто-то это делает! – DKR

1

Теоретически, ваш код подвержен неопределенному поведению.

Вы передали неинициализированный d по номеру Foo::Foo(). У вас нет никаких проблем, так как Dog не имеет переменных-членов, а Dog::dfoo() не зависит от значения каких-либо переменных-членов.

Если добавить переменную-член в Dog и использовать эту переменную-член в Dog::dfoo(), вы можете увидеть странный вывод:

class Dog { 
    int v1; 
    public: 
     Dog() {cout << "\n In Dog";} 
     void dfoo(){cout << "\n In dfoo, v1: " << v1 << endl;}; 
}; 
+0

Выход с вашим изменением: ' В dfoo, v1: 0 ' – DKR

+0

@DKR, к сожалению, это характер неопределенного поведения. Вы можете увидеть, казалось бы, здравый результат с одним компилятором и одним параметром, но это изменится с помощью другого компилятора и/или другого параметра компилятора. –

+0

Если вы создадите его в режиме отладки, ваш компилятор может инициализировать все переменные-члены до некоторого значения по умолчанию (например, '0'). Попробуйте это в режиме деблокирования. – Steve

0

Это является мусор.

Не уверен, какие видимые симптомы вы ожидали от этого. Для одного, Dog не имеет данных.

+0

Я ожидал некоторую ошибку для 1) передачи неинициализированного объекта 2) с использованием этого объекта. – DKR

+0

Я был, по крайней мере, ожидал этого http://stackoverflow.com/a/31300923/4478165 – DKR

+0

@ DKR: Ну, ваше ожидание было неправильным :) –

 Смежные вопросы

  • Нет связанных вопросов^_^