我有一个具有以下继承层次结构的库(模型):
struct Xbase {
virtual void f() = 0;
};
struct Derived1 : public Xbase {
void f() { std::cerr << "Derived1::f\n"; }
};
struct Derived2 : public Xbase {
void f() { std::cerr << "Derived2::f\n"; }
};
struct Storage {
std::map<int, Xbase*> data;
Xbase* get(int i) {
auto it = data.find(i);
return (it == data.end() ) ? nullptr : it->second;
}
};
库跟踪指向基本结构的指针,并允许在这个小例子中通过整数id检索它们。有一个应用程序使用该库,并希望通过引入第二个基类Ybase来提供一些功能:
struct Ybase {
virtual void g() { std::cerr << "Ybase::g\n"; }
};
struct AppDerived1 : public Derived1, public Ybase {
void f() { std::cerr << "AppDerived1::f\n"; }
};
struct AppDerived2 : public Derived2, public Ybase {
void f() { std::cerr << "AppDerived2::f\n"; }
};
当然比如
int i = 5;
Storage storage;
Xbase* xbase = storage.get(i);
Ybase* ybase = static_cast<Ybase *>(xbase);
效果不好
$ g++ -std=c++11 t.cpp
t.cpp: In function ‘int main()’:
t.cpp:21:45: error: invalid static_cast from type ‘Xbase*’ to type ‘Ybase*’
我想提供派生类的模板版本,以便app开发人员可以将他的类插入到库层次结构中。
库:
template<typename T>
struct Derived1 : public T {
void f() { std::cerr << "Derived1::f\n"; }
};
应用程序所有者:
struct Ybase : public Xbase {
virtual void g() { std::cerr << "Ybase::g\n"; }
};
struct AppDerived1 : public Derived1<Ybase> {
...
};
/* same for AppDerived2 */
Xbase* xbase = storage.get(i);
Ybase* ybase = static_cast<Ybase *>(xbase);
ybase->g();
这会创建一个单独的继承行,并且强制转换应该有效。
我想了解这是一个好主意还是一个坏主意,以及可能的替代方案。请注意,我确实需要层次结构中的公共基类Xbase,因为我必须能够从名称和ID等外部数据中检索基类指针。应用程序开发人员的问题是应用程序还需要其基本指针,因为应用程序不知道它接收的对象(AppDerived1或2)的类型。谢谢你的阅读。
答案 0 :(得分:1)
您无法将Xbase*
转换为Ybase*
,因为这些是不相关的类
但是,由于多重继承和动态转换,如果您知道可以使用的派生类最多,则可以安全地从Xbase转换为Ybase:
Ybase* ybase{};
Derived1 *pd1 = dynamic_cast<Derived1*>(xbase); // is it a Derived 1 ?
if (pd1) { // if yes
AppDerived1 *app = dynamic_cast<AppDerived1*>(pd1); // Then look for an AppDerived 1
if (app) // If valid cast
ybase = app; // we can cass the AppDerived1 to an Ybase
}
else { // same approach for the second type
Derived2 *pd2 = dynamic_cast<Derived2*>(xbase); // is it a Derived 2 ?
if (pd2) { // if yes
AppDerived2 *app = dynamic_cast<AppDerived2*>(pd2);
if (app)
ybase = app;
}
} // if ybase is still nullptr here, it means that we couldn't find a valid conversion path
cout << (ybase ? "Success " : "Fail ") << (void*)ybase << endl;
这可以利用您的对象结构和合法的上下铸件:
您的alernative是建立在将自己限制在单个继承层次结构的原则之上的。你班级的设计完全不同。您假设Ybase 是-a Xbase(始终):
这是否好是完全取决于您尝试表示的应用程序域。如果实际上Ybase总是一个Xbase,这将是完美的意义。
这里我只能引用Bjarne Stroustrup:
独立概念应该独立代表,应该是 仅在需要时合并。在违反这一原则的地方,你 要么将不相关的概念捆绑在一起,要么创造不必要 依赖。无论哪种方式,您都可以获得灵活性较低的组件集。