4

У меня следующая проблема:Виртуальные базовые классы Порядок создания

struct A1 { 
    A1() { std::cout << "A1, "; } 
}; 

struct A2 { 
    A2() { std::cout << "A2, "; } 
}; 

struct AA1 : virtual A1, A2 { 
    AA1() { std::cout << "AA1, "; } 
}; 

struct AA2 : A1, virtual A2 { 
    AA2(){ std::cout << "AA2, "; } 
}; 

struct B : AA1, virtual AA2 { 
    B() { std::cout << "B "; } 
}; 

int main() { 
    B b; 
} 

При выполнении этого кода, ответ:

A1 A2 A1 AA2 A2 AA1 B

Я хочу понять, где создается первый A1.

Я знаю правило, что виртуальные классы вызывают перед не-виртуальными классами, но первый A1 - проблема, которая беспокоит меня.

+3

Можете ли вы улучшить форматирование кода для удобства чтения? – WhiZTiM

+0

Виртуальные классы не называются. Вы не можете «вызвать класс». Скорее, * объекты инициализируются *. –

+0

Вы правы, я допустил ошибку. Спасибо за объяснение. –

ответ

2

Первый A1 является результатом инициализации (виртуальной) базы (не виртуальной) базы AA1 от B.

Все виртуальные основы B инициализируются первым, и они, в порядке, A1, A2 и AA2. (Инициализация AA2 приводит к результату A1 AA2.) Затем идут прямые базы, из которых только один, AA1 (чья инициализация печатает A2 AA1) и, наконец, сам класс, печать B. Сначала все виртуальные базы, а затем только оставшиеся не виртуальные.

+0

Большое вам спасибо.Это очень помогает мне. Я пытался понять эти виртуальные функции. Они были единственными, кого я не мог понять, и этот ответ мне очень помог. –

+0

Первый «А1» фактически является результатом виртуальной базы «AA1», а не из не виртуальной базы «AA2» (которая фактически является второй «А1»). Вы можете визуализировать это, добавив параметр в конструктор, указывающий, откуда они «вызваны». – Holt

+0

@ Холл: Да, конечно, чем вы очень! –

2

B имеет три виртуальных базовых классов: A1, A2 и AA2, и в соответствии с порядком их появления, он будет инициализировать их в таком порядке.

Первый A1 и A2 вы видите Инициализирующие виртуальной базы A1 и A2, но последняя виртуальная база AA2 имеет невиртуальную базу A1, поэтому перед созданием AA2, вам нужно построить еще A1, поэтому у вас есть еще A1 до AA2.

Вы можете визуализировать это, выполнив следующий фрагмент кода:

#include <iostream> 

struct A1 { 
    A1(const char *s) { std::cout << "A1(" << s << ")\n"; } 
}; 

struct A2 { 
    A2(const char *s) { std::cout << "A2(" << s << ")\n"; } 
}; 

struct AA1 : virtual A1, A2 { 
    AA1(const char *s) : A1("AA1"), A2("AA1") { std::cout << "AA1(" << s << ")\n"; } 
}; 

struct AA2 : A1, virtual A2 { 
    AA2(const char *s) : A1("AA2"), A2("AA2") { std::cout << "AA2(" << s << ")\n"; } 
}; 

struct B : AA1, virtual AA2 { 
    B() : A1("B"), A2("B"), AA1("B"), AA2("B") { std::cout << "B()\n"; } 
}; 

int main() { 
    B b; 
} 

Этот выход будет:

A1(B) 
A2(B) 
A1(AA2) 
AA2(B) 
A2(AA1) 
AA1(B) 
B() 

Вы также можете заметить, что этот код дает предупреждение, потому что:

  • Я положил A1 до A2 в конструктор AA2, но A2 будет инициализирован до A1 (потому что это виртуальная база и A1 нет).
  • Я положил AA1 до AA2 в конструктор B, но сначала будет инициализирован AA2 (по той же причине).