Сложная проблема с бриллиантами: виртуальное наследование C ++

У меня проблема с бриллиантами, которая выглядит так:

    __ A
/    |\
|  B  |  \
v|/v v\|v  \v
B2   B3    C
\v  /v   /
B4    /
\   /
D

Я пробовал много способов сделать лучшее виртуальное наследование, чтобы не было дубликатов, но я не мог найти решение. Класс А содержит позицию. Вот пример вывода:

Call: A() position pointer is: 0x2203be8
Call: B()
Call: B2() position pointer is: 0x2203be8
Call: B3() position pointer is: 0x2203be8
Call: C() position pointer is: 0x2203a28
Call: B4() position pointer is: 0x2203be8
Call: D() position pointer is: 0x2203a28

Почему D и C не имеют одинаковый указатель для позиции? Почему нет конструктора для этой позиции A ::? Какое виртуальное наследование я должен сделать, чтобы решить это? Благодарю.

РЕДАКТИРОВАТЬ:

Вот пример кода:

class A;
class B;
class B2 : public virtual B, public virtual A;
class B3 : public virtual B, public virtual A;
class C : public virtual A;
class B4 : public virtual B2, public virtual B3;
class D : public B4, public C;

РЕДАКТИРОВАТЬ 2:
Чтобы сделать вывод, я поместил этот код внутри каждого конструктора:

A::A()
{
std::cerr << "Call: A() position pointer is: " << &_position << std::endl;
}

5

Решение

Поскольку вы говорите, что приведенный ниже код, который работает на моих реализациях, для вас не работает, то, очевидно, код не является проблемой. Проблема с чем-то еще в вашей настройке; возможно ошибка компилятора. Вы должны сузить, что еще может быть причиной проблемы; поскольку сам код исключен как проблема, возможно, лучший следующий шаг — обновить ваш компилятор.

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


Я пытаюсь воспроизвести вашу проблему. Вот код, который я использую:

#include <iostream>

struct A { int a; };
struct B { int b; };
struct B2 : virtual B, virtual A {};
struct B3 : virtual B, virtual A {};
struct B4 : virtual B2, virtual B3 {}; // these virtuals are unnecessary in this case...
struct C : virtual A {};
struct D : B4, C {};

int main() {
D d;
std::cout << &((B4*)&d)->a << '\n';
std::cout << &((B3*)(B4*)&d)->a << '\n';
std::cout << &((B2*)(B4*)&d)->a << '\n';
std::cout << &((A*)(B2*)(B4*)&d)->a << '\n';
std::cout << &((A*)(B3*)(B4*)&d)->a << '\n';
std::cout << &((C*)&d)->a << '\n';
std::cout << &((A*)(C*)&d)->a << '\n';
}

Но результаты, которые я получаю, как и ожидалось, где a член одинаков для каждого объекта. Я получаю те же результаты, если использую print адреса в конструкторах: http://ideone.com/8FdQ1O

Если я сделаю небольшое изменение и уберу virtual Ключевое слово из определения C:

...
struct C : A {};
...

(версия с использованием конструкторов)

затем я вижу проблему, которую вы описываете, когда у C есть собственный объект A, отличный от виртуального объекта B2, B3 и B4.

Вы уверены, что используете virtual Ключевое слово во всех местах вам это нужно? Результаты, которые вы показываете, указывают на то, что вы где-то упускаете это Также отмечу, что вывод, который вы показываете, не отражает тот же порядок конструкторов, что и фрагменты кода, которые вы показываете; вывод показывает сначала A (), но код указывает, что B () должен быть выполнен первым.


Способ виртуального наследования заключается в том, что наиболее производный тип будет содержать один виртуальный подобъект для каждого типа, который фактически наследуется в любом месте дерева наследования. Кроме того, наиболее производный тип будет содержать подобъект для каждого экземпляра не виртуального наследования:

struct A {};
struct B : virtual A {};
struct C : A, B {};
struct D : virtual A, C {};
struct E : A, D {};
struct F : virtual A, E {};
struct G : A, F {};

G g;

g содержит в общей сложности четыре A подобъекты; по одному на каждый раз A фактически не наследуется (в C, E, а также G) и один раз за все времена A фактически наследуется (в B, D, а также F).

4

Другие решения

Какой код у вас сейчас есть? Похоже, что решение будет:

class D;
class C : public virtual D;
class B4 : public virtual D;
class B2 : public virtual B4;
class B3 : public virtual B4;
class B : public B2, public B3;
class A : public B2, public B3, public C;

на основе вашей диаграммы. Если я читаю это неправильно, и А является основой, а не D. Тогда это должно выглядеть так:

class A;
class B;
class B2 : public virtual B, public virtual A;
class B3 : public virtual B, public virtual A;
class C : public virtual A;
class B4 : public virtual B2, public virtual B3;
class D : public B4, public C;
1

Почему D и C не имеют одинаковый указатель для позиции?

Потому что вы не наследуете D от B4 и C. Это означает, что у вас есть две копии A (и два указателя).

In D конструктор &B4 :: позиция отличается от &C :: позиция

Почему нет конструктора для этой позиции A ::?

Не представляете, есть ли вероятность, что ваш класс A имеет более одного конструктора и что конструктор по умолчанию без вывода сообщений вызывается C :: C ()?

Какое виртуальное наследование я должен сделать, чтобы решить это?

Сделай все виртуальным. Это означает, что вам нужно явно вызывать каждый конструктор из D :: D () (то есть A :: A (), B :: B (), B2 :: B2 (), B3 :: B3 (), C :: С ()).

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

0
По вопросам рекламы [email protected]