C++:对象向量与指向新对象的指针向量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2693651/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 00:22:34  来源:igfitidea点击:

C++: Vector of objects vs. vector of pointers to new objects?

c++pointersvector

提问by metamemetics

I am seeking to improve my C++ skills by writing a sample software renderer. It takes objects consisting of points in a 3d space and maps them to a 2d viewport and draws circles of varying size for each point in view. Which is better:

我正在寻求通过编写示例软件渲染器来提高我的 C++ 技能。它获取由 3d 空间中的点组成的对象,并将它们映射到 2d 视口,并为视图中的每个点绘制不同大小的圆圈。哪个更好:

class World{
    vector<ObjectBaseClass> object_list;
public:
    void generate(){
        object_list.clear();
        object_list.push_back(DerivedClass1());
        object_list.push_back(DerivedClass2());

or...

或者...

class World{
    vector<ObjectBaseClass*> object_list;
public:
    void generate(){
        object_list.clear();
        object_list.push_back(new DerivedClass1());
        object_list.push_back(new DerivedClass2());

?? Would be using pointers in the 2nd example to create new objects defeat the point of using vectors, because vectors automatically call the DerivedClass destructors in the first example but not in the 2nd? Are pointers to new objects necessary when using vectors because they handle memory management themselves as long as you use their access methods? Now let's say I have another method in world:

?? 在第二个示例中使用指针来创建新对象会破坏使用向量的意义,因为向量会在第一个示例中自动调用 DerivedClass 析构函数,但在第二个示例中不会?使用向量时是否需要指向新对象的指针,因为只要您使用它们的访问方法,它们就会自行处理内存管理?现在假设我在世界上有另一种方法:

void drawfrom(Viewport& view){
    for (unsigned int i=0;i<object_list.size();++i){
        object_list.at(i).draw(view);
    }
}

When called this will run the draw method for every object in the world list. Let's say I want derived classes to be able to have their own versions of draw(). Would the list need to be of pointers then in order to use the method selector (->) ?

调用时,它将为世界列表中的每个对象运行 draw 方法。假设我希望派生类能够拥有自己的 draw() 版本。为了使用方法选择器(->),列表是否需要是指针?

采纳答案by Maciej Hehl

You wont get what You want with this code

你不会用这个代码得到你想要的

class World{
    vector<ObjectBaseClass> object_list;
public:
    void generate(){
        object_list.clear();
        object_list.push_back(DerivedClass1());
        object_list.push_back(DerivedClass2());

What is going to happen is called object slicing. You will get a vector of ObjectBaseClass.

将要发生的事情称为对象切片。您将获得一个 ObjectBaseClass 向量。

To make polymorphism work You have to use some kind of pointers. There are probably some smart pointers or references in boost or other libraries that can be used and make the code much safer than the second proposed solution.

要使多态起作用,您必须使用某种指针。boost 或其他库中可能有一些智能指针或引用可以使用,并使代码比第二个建议的解决方案更安全。

回答by Bj?rn Pollex

Since you are explicitly stating you want to improve your C++, I am going to recommend you start using Boost. This can help you with your problem in three different ways:

由于您明确表示要改进 C++,因此我建议您开始使用Boost。这可以通过三种不同的方式帮助您解决问题:

Using a shared_ptr

用一个 shared_ptr

Using a shared_ptrcould declare your vector like this:

使用 ashared_ptr可以像这样声明你的向量:

std::vector< boost::shared_ptr< ObjectBase > > object_list;

And use it like this:

并像这样使用它:

typedef std::vector< boost::shared_ptr< ObjectBase > >::iterator ObjectIterator;

for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
    (*it)->draw(view);

This would give you polymorphism and would be used just like it was a normal vector of pointers, but the shared_ptrwould do the memory-management for you, destroying the object when the last shared_ptrreferencing it is destroyed.

这会给你多态性,就像它是一个普通的指针向量一样使用,但是它shared_ptr会为你做内存管理,在最后一次shared_ptr引用它时销毁对象。

Note about C++11:In C++11 shared_ptrbecame part of the standard as std::shared_ptr, so Boost is no longer required for this approach. However, unless you really need shared ownership, it is recommended you use std::unique_ptr, which was newly introduced in C++11.

关于 C++11 的注意事项:在 C++11 中shared_ptr成为标准的一部分std::shared_ptr,因此这种方法不再需要 Boost。但是,除非您确实需要共享所有权,否则建议您使用std::unique_ptrC++11 中新引入的 。

Using a ptr_vector

用一个 ptr_vector

Using a ptr_vectoryou would do it like this:

使用 aptr_vector你会这样做:

boost::ptr_vector< ObjectBase > object_list;

And use it like this:

并像这样使用它:

typedef boost::ptr_vector< ObjectBase >::iterator ObjectIterator;

for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
    (*it)->draw(view);

This would again be used like a normal vector of pointers, but this time the ptr_vectormanages the lifetime of your objects. The difference to the first approach is, that here your objects get destroyed when the vector gets destroyed, whereas above they may live longer than the container, if other shared_ptrs referencing them exist.

这将再次用作普通的指针向量,但这次它ptr_vector管理对象的生命周期。与第一种方法的不同之处在于,这里的对象在向量被销毁时被销毁,而在上面,如果shared_ptr存在其他引用它们的对象,则它们的寿命可能比容器长。

Using a reference_wrapper

用一个 reference_wrapper

Using a reference_wrapperyou would declare it like this:

使用 areference_wrapper你会像这样声明它:

std::vector< boost::reference_wrapper< ObjectBase > > object_list;

And then use it like this:

然后像这样使用它:

typedef std::vector< boost::reference_wrapper< ObjectBase > >::iterator 
    ObjectIterator;

for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
    it->draw(view);

Notice that you do not have to dereference the iterator first as in the above approaches. This does however only work if the lifetime of your objects is managed elsewhere and is guaranteed to be longer than that of the vector.

请注意,您不必像上述方法那样首先取消引用迭代器。但是,这仅在您的对象的生命周期在其他地方进行管理并且保证比vector.

Note about C++11:reference_wrapperhas also been standardized in C++11 and is now usable as std::reference_wrapperwithout Boost.

关于 C++11 的注意事项:reference_wrapper也已在 C++11 中标准化,现在可以在std::reference_wrapper没有 Boost 的情况下使用。

As pointed out in Maciej Hs answer, your first approach results in object slicing. In general you may want to look into iteratorswhen using containers.

正如Maciej H的回答所指出的,您的第一种方法会导致object slicing。通常,您可能希望在使用容器时查看迭代器

回答by Tyler McHenry

As for your first question, it is generally preferred to use automatically allocated objects rather than dynamically allocated objects (in other words, notto store pointers) so long as for the type in question, copy-construction and assignment is possible and not prohibitively expensive.

至于你的第一个问题,通常最好使用自动分配的对象而不是动态分配的对象(换句话说,存储指针),只要有问题的类型,复制构造和赋值是可能的,并且不会过于昂贵.

If the objects can't be copied or assigned, then you can't put them directly into a std::vectoranyway, and so the question is moot. If the copying and/or assignment operations are expensive (e.g. the object stores a large amount of data), then you might want to store pointers for efficiency reasons. Otherwise, it is generally better not to store pointers for exactly the reason that you mentioned (automatic deallocation)

如果对象不能被复制或分配,那么你std::vector无论如何都不能将它们直接放入 a 中,所以这个问题没有实际意义。如果复制和/或赋值操作开销很大(例如对象存储大量数据),那么出于效率原因,您可能希望存储指针。否则,通常最好不要因为您提到的原因(自动释放)存储指针

As for your second question, yes, that is another valid reason to store pointers. Dynamic dispatch (virtual method calls) work only on pointers and references (and you can't store references in a std::vector). If you need to store objects of multiple polymorphic types in the same vector, you muststore pointers in order to avoid slicing.

至于你的第二个问题,是的,这是存储指针的另一个正当理由。动态分派(虚拟方法调用)仅适用于指针和引用(并且您不能将引用存储在 a 中std::vector)。如果需要在同一个向量中存储多个多态类型的对象,则必须存储指针以避免切片。

回答by T.E.D.

Well, it depends on what you are trying to do with your vector.

嗯,这取决于你想用你的载体做什么。

If you don't use pointers, then it is a copyof the object you pass in that gets put on the vector. If it is a simple object, and/or you don't want to bother with keeping track of the storage for them, this may be exactly what you want. If it is something complex, or very time-consuming to construct and destruct, you might prefer to do that work only once each and pass pointers into the vector.

如果您不使用指针,则它是您传入的对象的副本,并放在向量上。如果它是一个简单的对象,和/或您不想为跟踪它们的存储而烦恼,这可能正是您想要的。如果它是复杂的,或者构造和销毁非常耗时,您可能更愿意每次只执行一次该工作并将指针传递给向量。