有没有设计模式可用于实现这一点?
我的实现想法是:创建std::deque,std::deque等;存储std::list,其中包含来自deques的指针;我使用std::deque,因为它假定具有良好的内存管理和对象块。
最终答案是否定的。
多态只适用于非值类型:引用和指针。由于引用只能绑定一次,所以您不能真正将它们用于标准容器中。这就只剩下了指针。
您正在错误的方向上解决问题。如果您担心分配大量小对象的开销(我认为这是一个合理的担忧。也就是说,您有实际的分析数据或足够的经验来知道这对您的具体应用程序而言是个问题),那么您应该解决这个问题。改变如何为这些对象分配内存。创建一个小的分配堆之类的东西。
不可否认,在 C++0x 之前的分配器在这方面有些欠缺,因为它们必须是无状态的。但是对于您的目的,您应该能够处理它。
从您的编辑内容:
这是一个糟糕的主意。从std::deque
的任何位置擦除都会使您std::list
中的每个指针无效。
根据您的评论,这个想法是可行的。然而,为不同种类的对象拥有所有这些不同的内存块似乎违反了继承的整个目的。毕竟,您不能只写一个新类型的Animal
,然后将其插入std::list
;您必须为其提供内存管理。
您确定继承基础的多态是您在这里需要的吗?您确定其他方法不会同样有效吗?
class Animal {
public:
virtual void eat() = 0;
};
class Cat : public Animal {
virtual void eat() final override {
std::cout << "Mmh, tasty fish!" << std::endl;
}
};
class Dog: public Animal {
virtual void eat() final override {
std::cout << "Mmh, tasty bone!" << std::endl;
}
};
示例变体/访问者:
typedef boost::variant<Cat, Dog> AnimalVariant;
class AnimalVisitor : public boost::static_visitor<Animal&> {
public:
Animal& operator()(Cat& a) const {
return a;
}
Animal& operator()(Dog& a) const {
return a;
}
};
示例用法:
std::vector<AnimalVariant> list;
list.push_back(Dog());
list.emplace_back(Cat());
for(int i = 0; i < 5; i++) {
for(auto& v : list) {
Animal& a = v.apply_visitor(AnimalVisitor());
a.eat();
}
}
输出示例
Mmh, tasty bone!
Mmh, tasty fish!
Mmh, tasty bone!
Mmh, tasty fish!
Mmh, tasty bone!
Mmh, tasty fish!
Mmh, tasty bone!
Mmh, tasty fish!
Mmh, tasty bone!
Mmh, tasty fish!
sigma
(标准偏差)元函数在编译时进行评估,并使用mpl::if_
或其他方式选择,或者使用SFINAE或简单的特化,以使用变体实现低sigma,而对于高sigma则使用池。 - v.oddounew
的用法都不被鼓励...但是Boost在微控制器上不可用,所以我写了自己的variant
类。 - DarthRubikAnimalVisitor
的定义并使用lambda表达式:Animal& a = v.apply_visitor([](const auto& pet) -> Animal& { return pet; });
- F Pereirastd::vector<Dog> dogs;
std::vector<Cat> cats;
std::vector<Animal*> animals;
void addDog(Dog& dog, std::vector<Dog>& dogs, std::vector<Animal*>& animals) {
dogs.push_back(dog);
animals.push_back(&dog);
}
variant
向量来实现。 - v.oddoudogs.push_back(dog)
会复制一份狗的副本。被推入到动物列表中的指针指向的是参数所引用的原始狗,而不是同一只狗!这个指针可能会因为调用者的操作而失效。 - Andy Borrellanimals.push_back(&dog);
应该改为 animals.push_back(dogs.back());
,原因在上面的注释中已经提到。 - gmargari这是对使用变体(无论是Boost的还是自c++17以来STL的变体)的建议进行跟进,如果所有可能的子类在编译时已知(这种情况也称为“封闭集合多态性”)。
必须使用访问者模式来访问对象接口有点烦人(在我看来),这就是为什么我编写了一种变体类型,它公开了基类接口(甚至是基本类型运算符)。 它可以在https://github.com/Krzmbrzl/polymorphic_variant找到(需要C++17,因为它是围绕std::variant
构建的)。
克隆@Draziv的示例:
class Animal {
public:
virtual void eat() = 0;
};
class Cat : public Animal {
virtual void eat() final override {
std::cout << "Mmh, tasty fish!" << std::endl;
}
};
class Dog: public Animal {
virtual void eat() final override {
std::cout << "Mmh, tasty bone!" << std::endl;
}
};
pv::polymorphic_variant< Animal, Dog, Cat > variant;
variant->eat();
variant = Cat{};
variant->eat();
输出:
Mmh, tasty bone!
Mmh, tasty fish!
char
数组,并使用放置 new,但请记住,使用 new 在堆上创建许多小对象比使用 new 在堆上创建许多大对象更好。而且,如果不使用指针,唯一使用多态的方法是使用引用。 - Seth Carnegie