继承类类型的c ++容器
Posted
技术标签:
【中文标题】继承类类型的c ++容器【英文标题】:c++ container of inherited class types 【发布时间】:2012-10-10 11:33:56 【问题描述】:我想要一个容器(比如说一个 std::vector)来保存各种继承的类型,并实例化它们,即类的向量 --> 对象的向量。
例如:
class A;
class B: public class A
;
class C: public class A
;
void main()
std::vector<of inherited A types> typesVec;
std::vector<A*> objectsVec;
typesVec.push_back(class B);
typesVec.push_back(class C);
for (int i = 0; i < typesVec.size(); i++)
A* pA = new typesVec.at(i);
objectsVec.push_back(pA);
提前谢谢..
【问题讨论】:
需要动态填写typesVec
吗?也就是说,您想在运行时包含还是排除某些类型?
你很快就接受了答案......
【参考方案1】:
这在 C++ 中是不可能的(至少不是直接的)。我可以看到这种情况发生在具有反射的语言中,但 C++ 没有。
您可以做的是创建一个工厂或简单的方法来创建指定类型的对象。
您将拥有一个对象生成器的向量,而不是一个类型向量(足够接近,对吗?):
class A;
class B: public class A
;
class C: public class A
;
struct AFactory
virtual A* create() return new A;
;
struct BFactory : AFactory
virtual A* create() return new B;
;
struct CFactory : AFactory
virtual A* create() return new C;
;
//...
typesVec.push_back(new BFactory);
typesVec.push_back(new CFactory);
for (int i = 0; i < typesVec.size(); i++)
A* pA = typesVec.at(i)->create();
objectsVec.push_back(pA);
【讨论】:
“对象生成器”也称为工厂。 @AlexandreC。 “您可以做的是创建一个 工厂 或简单地创建指定类型的对象的方法。” 卢锡安,非常感谢!我被困了一段时间......我确实最终使用了一种工厂,尽管略有不同。【参考方案2】:模板有一种可重用的方法。这是派生类型的通用工厂,带有 install
和 create
方法,可让您编写如下代码:
int main()
TypeVector<Base> t;
t.install<Foo>("Foo");
t.install<Bar>("Bar");
t.create("Foo")->hello();
请注意,这是一个草图实现。在现实世界中,您可以提供另一个模板参数来指定底层容器类型(对于少数类型,vector
可能比set
更有效)。
类型向量是这样的:
template <typename Base>
class Creator;
template <typename Base>
class TypeVector
public:
template <typename Derived>
void install (std::string const &name) ;
std::shared_ptr<Base> create (std::string const &name) const;
private:
struct Meta
Meta(std::shared_ptr<Creator<Base>> creator, std::string const &name)
: creator(creator), name(name)
std::shared_ptr<Creator<Base>> creator;
std::string name;
;
std::vector<Meta> creators_;
;
我们需要一种以可分配方式存储类型的方法。我们像boost::shared_ptr
那样做,它结合了一个抽象基类和一个模板派生类:
template <typename Base>
class Creator
public:
virtual ~Creator()
virtual std::shared_ptr<Base> create() const = 0;
;
template <typename Base, typename Derived>
class ConcreteCreator : public Creator<Base>
public:
virtual std::shared_ptr<Base> create() const
return std::shared_ptr<Base>new Derived();
;
“具体的创建者”能够分配一个实际的对象,并返回一个指向它的指针。
最后,这里是TypeVector::install
和TypeVector::create
的实现:
template <typename Base>
template <typename Derived>
void
TypeVector<Base>::install (std::string const &name)
creators_.emplace_back(
std::shared_ptr<Creator<Base>>(new ConcreteCreator<Base, Derived>()),
name);
template <typename Base>
std::shared_ptr<Base>
TypeVector<Base>::create (std::string const &name) const
for (auto m : creators_)
if (name == m.name) return m.creator->create();
throw std::runtime_error("...");
最后,这是一个测试:
#include <iostream>
struct Base
virtual ~Base()
virtual void hello() const = 0;
;
struct Foo : Base
virtual void hello() const std::cout << "I am a Foo\n";
;
struct Bar : Base
virtual void hello() const std::cout << "I am a Bar\n";
;
int main()
TypeVector<Base> t;
t.install<Foo>("Foo");
t.install<Bar>("Bar");
t.create("Foo")->hello();
您可以更进一步,让任何构造函数都可调用,例如 ...
...
Bar(Color, Age, int)
...
t.create("Foo", Color::Red, Age::TooOld, 42)
...但这需要对可变参数模板参数列表以及如何将它们折叠到构造函数调用中(可以完成并且已经完成,但它会爆炸这个答案)的掌握。
【讨论】:
Sebastian,据您所知,是否有从t.create("Foo")
?i.e. 获取类型信息的最佳实践?此信息允许使用标签调度并保持所有值类型。【参考方案3】:
只是一个快速的解决方案草图:
C++ 标准不提供对构造函数的直接调用。因此,您不能拥有指向构造函数的函数指针;但是,您可以使用包装函数“create”,例如:
template<typename T>
T* create ()
return (new T();
为一个参数、两个参数...提供重载的创建定义,或者尝试使用可变参数模板;或者,如果您已经知道需要什么类型,则可以专门创建创建函数。然后你可以有一个指向创建函数的函数指针:
&create<TheType>
请注意,此函数的签名取决于所使用的类型。但是,您可以创建一个结构,其中包含模板类型的 typdef、类型指针的 typedef,以及作为仿函数 operator()
的 create 函数。
因此,您可以有两个向量,一个用于指向创建函数的函数指针,或者替代前面提到的结构,另一个用于实际对象。在您只有继承类型的情况下,您可能能够为每个继承类型 B、C、... 定义函数 A* createB() return new B();
、A* createC() return new C();
、...,并为指向这些创建函数的指针提供一个向量和A 指针的第二个向量。
【讨论】:
【参考方案4】:我可能会指出 Andrei Alesandrescu 的书 Modern C++ Design(或他在书中描述的 Loki 库)和关于类型列表的章节。这将要求您在编译时执行typeVec.insert( type )
。
【讨论】:
以上是关于继承类类型的c ++容器的主要内容,如果未能解决你的问题,请参考以下文章
基于 C++17 中的范围,用于自定义容器或具有不同开始/结束类型的通用类
c_cpp C ++的序列容器类,其作用类似于可以连接的字符串,但适用于任何类型。