c++ 库:应用程序在继承层次结构中插入 class
c++ library: application inserts a class in inheritance hierarchy
我有一个具有以下继承层次结构的库(模型):
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。有一个使用该库的应用程序,并希望通过引入第二个基 class 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*’
我想提供派生的 classes 的模板版本,以便应用程序开发人员可以将他的 class 插入到库层次结构中。
图书馆:
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();
这将创建一个单一的继承线,转换应该有效。
我想了解这是好主意还是坏主意,还有什么替代方案。请注意,我确实需要层次结构中的公共基 class Xbase,因为我必须能够从名称和 ID 等外部数据中检索基 class 指针。应用程序开发人员的问题是应用程序还需要其基指针,因为应用程序不知道它接收到的对象的类型(AppDerived1 或 2)。感谢阅读。
初步评论:
您不能将 Xbase*
转换为 Ybase*
,因为它们是不相关的 class。
但是,多亏了多重继承和动态转换,如果您知道可以使用的最派生的 classes,您可以安全地从 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;
这可以利用您的对象结构和合法的上下转换:
对您的备选方案的评估:
您的替代方案建立在将自己限制在单一继承层次结构的原则之上。你的 class 的设计完全不同。你假设一个 Ybase is-a Xbase (always):
这是否好完全取决于您尝试表示的应用程序域。如果在现实中 Ybase 始终是 Xbase,这将非常有意义。
这里我只能引用 Bjarne Stroustrup:
Independent concepts should be independently represented and should be
combined only when needed. Where this principle is violated, you
either bundle unrelated concepts together, or create unnecessary
dependencies. Either way you get less flexible set of components.
我有一个具有以下继承层次结构的库(模型):
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。有一个使用该库的应用程序,并希望通过引入第二个基 class 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*’
我想提供派生的 classes 的模板版本,以便应用程序开发人员可以将他的 class 插入到库层次结构中。
图书馆:
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();
这将创建一个单一的继承线,转换应该有效。
我想了解这是好主意还是坏主意,还有什么替代方案。请注意,我确实需要层次结构中的公共基 class Xbase,因为我必须能够从名称和 ID 等外部数据中检索基 class 指针。应用程序开发人员的问题是应用程序还需要其基指针,因为应用程序不知道它接收到的对象的类型(AppDerived1 或 2)。感谢阅读。
初步评论:
您不能将 Xbase*
转换为 Ybase*
,因为它们是不相关的 class。
但是,多亏了多重继承和动态转换,如果您知道可以使用的最派生的 classes,您可以安全地从 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;
这可以利用您的对象结构和合法的上下转换:
对您的备选方案的评估:
您的替代方案建立在将自己限制在单一继承层次结构的原则之上。你的 class 的设计完全不同。你假设一个 Ybase is-a Xbase (always):
这是否好完全取决于您尝试表示的应用程序域。如果在现实中 Ybase 始终是 Xbase,这将非常有意义。
这里我只能引用 Bjarne Stroustrup:
Independent concepts should be independently represented and should be combined only when needed. Where this principle is violated, you either bundle unrelated concepts together, or create unnecessary dependencies. Either way you get less flexible set of components.