Еще раз о виртуальной базе при одиночном наследовании
От: PVA  
Дата: 20.11.06 08:45
Оценка:
Привет,

помогите объяснить поведение следующего кода:
#include <string>
#include <iostream>

using namespace std;

class base
{
public:
    base()
    {
        cout << "base::default" << endl;
    }

    base(const char * name) :
        name_(name)
    {
        cout << "base::name" << endl;
    }

    string name_;
};

class derived :
    virtual public base
{
public:
    derived() :
        base()
    {
        cout << "derived::default" << endl;
    }

    derived(const char * name) :
        base(name)
    {
        cout << "derived::name" << endl;
    }
};

class leaf :
    public derived
{
public:
    leaf() :
        derived()
    {
        cout << "leaf::default" << endl;
    }

    leaf(const char * name) :
        derived(name)
    {
        cout << "leaf::name" << endl;
    }
};

int main(int argc, char* argv[])
{
    derived d("Ok");
    leaf    l("WTF?");

    cout << "derived::name_: " << d.name_.c_str() << endl;
    cout << "leaf::name_: " << l.name_.c_str() << endl;

    return 0;
}


Интересует почему в случае с leaf нужный конструктор derived(name) вызывается, но инициализации base не происходит?

p.s. Что инициализировать нужно базу — это знаю Вопрос исключительно теоретический.
newbie
Re: Еще раз о виртуальной базе при одиночном наследовании
От: Андрей Тарасевич Беларусь  
Дата: 20.11.06 08:57
Оценка:
Здравствуйте, PVA, Вы писали:

PVA>Интересует почему в случае с leaf нужный конструктор derived(name) вызывается, но инициализации base не происходит?

PVA>p.s. Что инициализировать нужно базу — это знаю Вопрос исключительно теоретический.

Если "знаю", то тогда я не совсем понимаю, в чем именно вопрос. Почему так сделано? Потому что такого поведения требует спецификация языка.
Best regards,
Андрей Тарасевич
Re: Еще раз о виртуальной базе при одиночном наследовании
От: Bell Россия  
Дата: 20.11.06 08:58
Оценка:
Здравствуйте, PVA, Вы писали:

PVA>Интересует почему в случае с leaf нужный конструктор derived(name) вызывается, но инициализации base не происходит?

Виртуальную базу должен инициализировать most-derived класс.
Любите книгу — источник знаний (с) М.Горький
Re[2]: Еще раз о виртуальной базе при одиночном наследовании
От: PVA  
Дата: 20.11.06 09:07
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

PVA>>Интересует почему в случае с leaf нужный конструктор derived(name) вызывается, но инициализации base не происходит?

PVA>>p.s. Что инициализировать нужно базу — это знаю Вопрос исключительно теоретический.

АТ>Если "знаю", то тогда я не совсем понимаю, в чем именно вопрос. Почему так сделано? Потому что такого поведения требует спецификация языка.

Вопрос в том, почему вот здесь
derived() : base(name) {}

тело отрабатывает, а список инициализации игнорируется?

Например, если я инициалищацию перенесу в тело конструктора — свойство будет проинициализировано.
newbie
Re[2]: Еще раз о виртуальной базе при одиночном наследовании
От: PVA  
Дата: 20.11.06 09:07
Оценка:
Здравствуйте, Bell, Вы писали:

PVA>>Интересует почему в случае с leaf нужный конструктор derived(name) вызывается, но инициализации base не происходит?

B>Виртуальную базу должен инициализировать most-derived класс.
См. PS
newbie
Re: Еще раз о виртуальной базе при одиночном наследовании
От: MasterZiv СССР  
Дата: 20.11.06 09:12
Оценка:
PVA>Интересует почему в случае с leaf нужный конструктор derived(name) вызывается, но инициализации base не происходит?

Как это — нужный конструктор вызывается, но инициализация не происходит ?

Инициализация по вашему это что, кроме как не вызов конструктора ?

На самом деле вызов конструктора base НЕ будет производиться из вызова конструктора derived,

все вызовы конструкторов виртуальных баз должны быть ЯВНО прописаны в каждом из наследников, потому что по дереву наследования, поскольку есть несколько путей к предку, невозможно однозначно определить место, в котором должен следовать вызов конструктора виртуалной базы в порядке следования вызовов конструкторов предков.
Re[3]: Еще раз о виртуальной базе при одиночном наследовании
От: Bell Россия  
Дата: 20.11.06 09:16
Оценка:
Здравствуйте, PVA, Вы писали:

B>>Виртуальную базу должен инициализировать most-derived класс.

PVA>См. PS

Ок, еще раз:
Виртуальную базу инициализирует most-derived класс. Это значит, что при конструировании объекта leaf сигнатура вызываемого конструктора base определяется в конструкторе класса leaf, а не derived. Поскольку в списке инициализации leaf отсутсвует упоминание base, для конструирования подобъекта base будет выбран конструктор по умолчанию. При этом совершенно не важно, что для конструирования derived указан конструктор с параметром. derived в данном случае не является most-derived классом, и компиятору совершенно безразлично то, какой контсруктор для base указан в конструкторе derived.
Любите книгу — источник знаний (с) М.Горький
Re[4]: Еще раз о виртуальной базе при одиночном наследовании
От: Bell Россия  
Дата: 20.11.06 09:19
Оценка:
Здравствуйте, Bell, Вы писали:

В качестве дополнения: на этом свойстве основан прием "Запретить наследование"
Автор: orangy
Дата: 23.09.02
Любите книгу — источник знаний (с) М.Горький
Re[2]: Еще раз о виртуальной базе при одиночном наследовании
От: PVA  
Дата: 20.11.06 09:30
Оценка:
Здравствуйте, MasterZiv, Вы писали:

PVA>>Интересует почему в случае с leaf нужный конструктор derived(name) вызывается, но инициализации base не происходит?


MZ>Как это — нужный конструктор вызывается, но инициализация не происходит ?

MZ>Инициализация по вашему это что, кроме как не вызов конструктора ?
Попробую еще детальней
1. Происходит вызов base() — чудненько, base constructed
2. Происходит вызов параметризированного конструктора derived(const char * name), у которого параметром идет переданная строка "WTF?"
Дальше не интересно. Интересно, почему игнорируется вызов : base(name) из списка инициализации derived(...)?

MZ>На самом деле вызов конструктора base НЕ будет производиться из вызова конструктора derived,

Вот в этом и вопрос — почему?

MZ>все вызовы конструкторов виртуальных баз должны быть ЯВНО прописаны в каждом из наследников, потому что по дереву наследования, поскольку есть несколько путей к предку, невозможно однозначно определить место, в котором должен следовать вызов конструктора виртуалной базы в порядке следования вызовов конструкторов предков.

Совершенно с этим согласен и не спорю.

В целом можно считать вопрос закрытым. Странно, что компилер даже ворниг не выдает.
newbie
Re[5]: Еще раз о виртуальной базе при одиночном наследовании
От: PVA  
Дата: 20.11.06 09:32
Оценка:
Здравствуйте, Bell, Вы писали:

B>В качестве дополнения: на этом свойстве основан прием "Запретить наследование"
Автор: orangy
Дата: 23.09.02

Ага, читал. Не очень красивый трюк
newbie
Re[3]: Еще раз о виртуальной базе при одиночном наследовании
От: Bell Россия  
Дата: 20.11.06 09:56
Оценка:
Здравствуйте, PVA, Вы писали:

PVA>Попробую еще детальней

PVA>1. Происходит вызов base() — чудненько, base constructed
PVA>2. Происходит вызов параметризированного конструктора derived(const char * name), у которого параметром идет переданная строка "WTF?"
PVA>Дальше не интересно. Интересно, почему игнорируется вызов : base(name) из списка инициализации derived(...)?
Потому что base уже сконструирован, и конструктор для base был выбран в конструкторе leaf.

MZ>>На самом деле вызов конструктора base НЕ будет производиться из вызова конструктора derived,

PVA>Вот в этом и вопрос — почему?
Потому что derived не является most-derived классом (я это уже не в первый раз повторяю).


PVA>В целом можно считать вопрос закрытым.

Как Вам угодно.

PVA>Странно, что компилер даже ворниг не выдает.

Какое предупреждение, по Вашему, должен был выдать компилятор?
Любите книгу — источник знаний (с) М.Горький
Re[4]: Еще раз о виртуальной базе при одиночном наследовании
От: Анатолий Широков СССР  
Дата: 20.11.06 10:03
Оценка: :)
PVA>>Странно, что компилер даже ворниг не выдает.
B>Какое предупреждение, по Вашему, должен был выдать компилятор?

Ну, "ты туда не ходи, ты сюда ходи. там снег башка попадет.."
Re[3]: Еще раз о виртуальной базе при одиночном наследовании
От: Андрей Тарасевич Беларусь  
Дата: 20.11.06 10:14
Оценка: 1 (1)
Здравствуйте, PVA, Вы писали:

PVA>Здравствуйте, Андрей Тарасевич, Вы писали:


PVA>>>Интересует почему в случае с leaf нужный конструктор derived(name) вызывается, но инициализации base не происходит?

PVA>>>p.s. Что инициализировать нужно базу — это знаю Вопрос исключительно теоретический.

АТ>>Если "знаю", то тогда я не совсем понимаю, в чем именно вопрос. Почему так сделано? Потому что такого поведения требует спецификация языка.

PVA>Вопрос в том, почему вот здесь
PVA>
derived() : base(name) {}

PVA>тело отрабатывает, а список инициализации игнорируется?

Я по-прежнему не понимаю. Что значит "почему"? В спецификации языка ясно сказано, что виртуальные базы инициализирует "финальный" класс, т.е. что промежуточные классы-наследники не должны инициализировать свои виртуальные базы, независимо от того, что там написано в списке инициализации.

Компиляторы обязаны удовлетворить этому требованию спецификации. Как они будут ему удовлетворять — это уже их личное дело. Можно преположить множество разных способов. Например, компилятор может генерировать две версии конструктора 'derived::derived(const char *)'. Одна версия "нормальная", вызывается при конструировании самостоятельных объектов 'derived' ('d' в твоем примере). Другая версия "урезанная", не производящая инициализации виртуальных баз. Эта "урезанная" версия вызвается из конструктора класса 'leaf'. Вот и все.

Компиляотр может поступить еще как-нибудь по-другому. Например, неявно передавать в конструктор булевский флаг — инициализировать/не инициализировать виртуальные базы. В общем, способов много...
Best regards,
Андрей Тарасевич
Re[4]: Еще раз о виртуальной базе при одиночном наследовании
От: PVA  
Дата: 20.11.06 10:18
Оценка:
Здравствуйте, Bell, Вы писали:

PVA>>Дальше не интересно. Интересно, почему игнорируется вызов : base(name) из списка инициализации derived(...)?

B>Потому что base уже сконструирован, и конструктор для base был выбран в конструкторе leaf.
MZ>>>На самом деле вызов конструктора base НЕ будет производиться из вызова конструктора derived,
PVA>>Вот в этом и вопрос — почему?
B>Потому что derived не является most-derived классом (я это уже не в первый раз повторяю).
С технической точки зрения это равносильно пропуску вызова функции.

PVA>>Странно, что компилер даже ворниг не выдает.

B>Какое предупреждение, по Вашему, должен был выдать компилятор?
warning XXXX: "список инициализации объекта leaf будет проигнорирован"
newbie
Re[5]: Еще раз о виртуальной базе при одиночном наследовании
От: Bell Россия  
Дата: 20.11.06 10:32
Оценка:
Здравствуйте, PVA, Вы писали:

PVA>С технической точки зрения это равносильно пропуску вызова функции.

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

PVA>>>Странно, что компилер даже ворниг не выдает.

B>>Какое предупреждение, по Вашему, должен был выдать компилятор?
PVA>warning XXXX: "список инициализации объекта leaf будет проигнорирован"
Он-то как раз и не игнорируется, и конструктор для base определяется именно в списке инициализации leaf. "Игнорируется" вызов конструктора base с параметром в конструкторе derived.
А вообще указание конструктора предка в списке инициализации не есть вызов — это просто указаение компилятору, какой именно конструктор предка использовать при конструировании потомка (читай при построении цепочки вызовов).
В случае с виртуальным наследованием это указание игнорируется для промежуточных классов, вот и все.
Любите книгу — источник знаний (с) М.Горький
Re[6]: Еще раз о виртуальной базе при одиночном наследовании
От: PVA  
Дата: 20.11.06 10:57
Оценка:
Здравствуйте, Bell, Вы писали:

PVA>>С технической точки зрения это равносильно пропуску вызова функции.

B>Никакого пропуска нет. Есть особенность, определяющая, какой именно конструктор нужно вызвать.
У меня посылки были:
1. конструктор всего-лишь функция инициализации данных.
2. всегда думал, что
derived(A a, B b) : a_(a), b_(b) {}

равносильно
derived(A a, B b) { a_ = a; b_ = b; }

Соответственно:
derived(const char * name) : base(name) { /* body */ } == derived() { base(name); /* body */ }

PVA>>>>Странно, что компилер даже ворниг не выдает.

B>>>Какое предупреждение, по Вашему, должен был выдать компилятор?
PVA>>warning XXXX: "список инициализации объекта leaf будет проигнорирован"
B>Он-то как раз и не игнорируется, и конструктор для base определяется именно в списке инициализации leaf. "Игнорируется" вызов конструктора base с параметром в конструкторе derived.
Да, несомненно. Вот об этом надо было бы и выдать варнинг (сменим табличку): "список инициализации объекта leaf отсутствует. Будет использован конструктор по умолчанию"

B>А вообще указание конструктора предка в списке инициализации не есть вызов — это просто указаение компилятору, какой именно конструктор предка использовать при конструировании потомка (читай при построении цепочки вызовов).

Кхм. Цепочки вызовов чего?

B>В случае с виртуальным наследованием это указание игнорируется для промежуточных классов, вот и все.

Вы даже лучше меня строку для варнинга сочинили :D
newbie
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.