没有指针的 C++ 多态性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7223613/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ polymorphism without pointers
提问by Andrei Bozantan
Suppose that I have a base class Animal
with virtual functions and some derived classes (Cat
, Dog
, etc.). The real derived classes contain 4-8 bytes of data. I want to store a std::list<Animal>
which actually contains items which are derived objects. I want to avoid the creation of many small objects on the heap using new.
假设我有一个基类Animal
与虚函数和一些派生类(Cat
,Dog
等)。真正的派生类包含 4-8 个字节的数据。我想存储一个std::list<Animal>
实际上包含派生对象的项目。我想避免使用 new 在堆上创建许多小对象。
Is there any design pattern which can be used to achieve this?
是否有任何设计模式可用于实现这一目标?
EDIT: My ideas to implement this
编辑:我的想法来实现这个
- create
std::deque<Cat>
,std::deque<Dog>
, ...; storestd::list<Animal*>
which contains pointers from thedeques
; I use thestd::deque
because I suppose that it has a good memory management with chunks of objects;
- 创建
std::deque<Cat>
,std::deque<Dog>
, ...;std::list<Animal*>
包含来自 的指针的存储deques
;我使用 是std::deque
因为我认为它对对象块具有良好的内存管理;
回答by Nicol Bolas
Ultimately, no.
最终,没有。
Polymorphism only works with non-value types: references and pointers. And since references can only be bound once, you cannot really use them in standard containers. That leaves you with pointers.
多态只适用于非值类型:引用和指针。而且由于引用只能绑定一次,因此您不能真正在标准容器中使用它们。这给你留下了指针。
You're attacking the problem at the wrong end. If you are concerned about the overhead of allocating lots of small objects (and I'm assuming that this is a legitimate concern. That is, you have actual profiling data or sufficient experience to knowit is a concern for your specificapplication), then you should fix that. Change how you're allocating memory for these objects. Make a small allocation heap or something.
你在错误的一端解决问题。如果您担心分配大量小对象的开销(我假设这是一个合理的问题。也就是说,您有实际的分析数据或足够的经验来知道这是您的特定应用程序的一个问题),那么你应该解决这个问题。更改为这些对象分配内存的方式。做一个小的分配堆什么的。
Admittedly, pre-C++0x's allocators are somewhat lacking in this regard, since they have to be stateless. But for your purposes, you should be able to deal with it.
诚然,C++0x 之前的分配器在这方面有些欠缺,因为它们必须是无状态的。但是为了您的目的,您应该能够处理它。
From your edit:
从您的编辑:
That is a terribleidea. Erasing from a std::deque
at anywherebut the start or end will invalidate everypointer in your std::list
.
这是一个可怕的想法。从删除std::deque
在任何地方,但起点或终点会作废每次在你的指针std::list
。
Given your comment, this idea is functional. However, having all of these different memory blocks for different kinds of objects seems to go against the whole point of inheritance. After all, you can't just write a new type of Animal
and slip it into the std::list
; you have to provide memory management for it.
鉴于您的评论,这个想法是实用的。然而,为不同类型的对象拥有所有这些不同的内存块似乎违背了继承的全部意义。毕竟,您不能只是编写一种新类型Animal
并将其插入std::list
; 你必须为它提供内存管理。
Are you sure that inheritance-based polymorphism is what you need here? Are you sure that some other methodology would not work just as well?
您确定这里需要基于继承的多态性吗?您确定其他一些方法也不会奏效吗?
回答by Draziw
I realize that this question is old, but I found a somewhat pretty solution.
我意识到这个问题很老,但我找到了一个相当不错的解决方案。
Assumption:
假设:
You know all the derived classes in advance (given your edit, this is true).
您事先知道所有派生类(根据您的编辑,这是真的)。
Trick:
诡计:
Using boost::variant (http://www.boost.org/doc/libs/1_57_0/doc/html/variant.html)
使用 boost::variant ( http://www.boost.org/doc/libs/1_57_0/doc/html/variant.html)
Example classes:
示例类:
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;
}
};
Example variant/visitor:
示例变体/访问者:
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;
}
};
Example usage:
用法示例:
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();
}
}
Example output
示例输出
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!
回答by Brian Coleman
If you're worried about allocating many small heap objects, then a vector may be a better choice of container rather than a list and a deque. list will allocate a node on the heap each time that you insert an object into the list, whereas vector will store all objects in a contiguous region of memory on the heap.
如果您担心分配许多小堆对象,那么向量可能是更好的容器选择,而不是列表和双端队列。每次将对象插入列表时,list 都会在堆上分配一个节点,而 vector 会将所有对象存储在堆上的连续内存区域中。
If you have:
如果你有:
std::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);
}
Then all dogs and cats are stored in two contiguous region of memory on the heap.
然后所有的狗和猫都存储在堆上两个连续的内存区域中。
回答by soru
You probably could do something with a simple wrapper class for a union containing the super-set of data needed by every case. This would contain a pointer to shared strategyobjects that contained the code for the different behaviours. So a cat is an object of class PolyAnimal with speciesName = "cat", a PredatorFeedingStrategy and so on.
您可能可以使用一个简单的包装类为包含每个案例所需的超集数据的联合做一些事情。这将包含一个指向包含不同行为代码的共享策略对象的指针。所以一只猫是 PolyAnimal 类的一个对象,有物种名称 = "cat",一个 PredatorFeedingStrategy 等等。
Likely a better way of solving the underlying problem is setting up appropriate custom allocatorsfor a more natural design.
解决潜在问题的更好方法可能是为更自然的设计设置适当的自定义分配器。