У меня есть класс D, который расширяет B, который расширяет A. Теперь я хочу добавить класс C, который имеет точно такой же интерфейс, что и B, но обеспечивает другую реализацию. Поэтому я спроектировал это следующим образом:
Это не совсем то, что я хочу, так как мне нужен только экземпляр D для расширения B или C, а не оба, однако, это определяется только во время выполнения. Проблема с дизайном выше состоит в том, что если я вызываю метод в D, который реализован как в B, так и в C, то он неоднозначен.
Поэтому я хотел бы создать экземпляр B или C во время выполнения и затем преобразовать его в D. Каждый раз, когда экземпляр D вызывает унаследованный метод, он должен использовать тот из своего исходного объекта.
Нужно ли возиться с typeid и if / else вокруг каждого вызова метода или есть более элегантный способ сделать это?
class A{
virtual f1();
virtual f2();
}
class B : public virtual A{
f1();
f2();
f3();
}
class C : public virtual A{
f1();
f2();
f3();
}
class D : public B, public C{
f4(){f1(); f3)};
}
...
D* d = new D();
E* e = new E(d);
e->d->f1();
e->d->f4();
Экземпляры D затем передаются другому классу (E), который работает с D, и поэтому я не могу изменить интерфейс D.
Я думаю, что у вас неправильное наследование, то, что вы делаете, это определяете все методы, которые вы хотите вызывать для того, что вы называете классом D, как виртуальные методы в классе A, классы B и C оба имеют свою собственную реализацию этих методов. ,
Затем вы используете структуру данных типа A *, заполняете ее указателями на объекты типа B и C и вызываете методы, которые вам нужно вызывать для всех объектов в структуре данных, содержащей указатели типа A *, vtable Затем механизм убедится, что реализация класса B или C используется в зависимости от того, какой тип объекта является действительным.
Увидеть В чем разница между конкретным классом и абстрактным классом?
Похоже, вы просто хотите
class A{
virtual void DoMagic() = 0;
};
class B{
virtual void DoMagic(){};
};
class D{
virtual void DoMagic(){};
};
...
bool INeedB = true;//or false
A* a;
if(INeedB){
a= new B();
}else{
a = new C();
}
a->DoMagic(); // will call the appropriate method based on the value of INeedB;
Разве D на самом деле имеет собственное поведение? Тогда вы можете посмотреть на шаблон декоратора, и сделать D декоратором экземпляра B или C.
Редактировать: Ваш D
класс не должен наследовать какой-либо из A
B
или же C
совсем.
class D{
D(A* aObj):a(aObj){}
void f3(){ a->f1();a->f2();}
A *a;
};
замещать A *a
в приведенном выше примере с D d
C ++ — это статически типизированный язык. Что бы вы ни делали с объявлением типа, оно разрабатывается во время компиляции, поэтому граф наследования D
нельзя игнорировать во время выполнения.
Вам, вероятно, нужно иметь A
как полиморфная база (со всеми соответствующими виртуальными методами, включая деструктор) как для B, так и для C (конкретная реализация этого), и D an «владелец A
«, содержащая A * thet будет назначена в конструкции D для new B
или же new C
в зависимости от ввода.
Д деструктор позвоню delete A
, и теперь вы должны принять решение о копировании и назначении.
Мое предложение не использовать A *, а std::unique_ptr
(сделает принадлежащий объект подвижным между D-s) или std::shared_ptr
,
В случае, если вам нужно, чтобы у каждого D была своя собственная A, пусть A имеет clone
метод (переопределяется в B и C, чтобы вернуть new B
а также new C
соответственно) и назовите его в экземпляре d ctor и операторе присвоения.
Это похоже на D
не нужно наследовать от A
(или же B
или же C
) совсем. Вместо этого ему просто нужно вызвать функцию в любом случае B
или экземпляр C
,
Вы можете реализовать это примерно так:
class A
{
public:
virtual void f1();
virtual void f2();
};
class B : public A;
class C : public A;
class D
{
A* b_or_c;
public:
D(A* a_pointer)
: b_or_c(a_pointer)
{}
void f3()
{
b_or_c->f1();
b_or_c->f2();
}
};
Можно использовать так:
B b; // An instance of B
C c; // An instance of C
D d1(&b);
D d2(&c);
d1.f3(); // Will cause `f1` and `f2` in the object `b` to be called
d2.f3(); // Will cause `f1` and `f2` in the object `c` to be called