简体   繁体   English

指针向量和值向量之间的差异

[英]Difference between vector of pointer and vector of values

I heard pointers are not recommended in C++ but I don't understand why.我听说在 C++ 中不推荐使用指针,但我不明白为什么。

My problem is I want to create a vector of class objects to manage my classes.我的问题是我想创建一个 class 对象的向量来管理我的类。

vector<MyClass> vectorOfClass;

Naturally, for better performance, I should go with a vector of a pointer of class objects?自然,为了获得更好的性能,我应该使用 go 与 class 对象的指针向量?

vector<MyClass *> vectorOfClass;

Or maybe is it possible to create a vector of reference of class objects?或者是否可以创建 class 对象的参考向量?

vector<MyClass &> vectorOfClass;

So my questions are:所以我的问题是:

  • What is the difference between these ways?这些方式有什么区别?
  • What is the most optimized to create a vector of class objects?创建 class 对象的向量最优化的是什么?

Or maybe is it possible to create a vector of reference of class objects?或者是否可以创建 class 对象的参考向量?

No. Reference are not objects in C++.不,引用不是 C++ 中的对象。 So you can't create arrays of reference or pointer to references.所以你不能创建 arrays 的引用或指向引用的指针。 You can however use a std::reference_wrapper , which wraps reference in an object.但是,您可以使用std::reference_wrapper ,它将引用包装在 object 中。

What is the most optimized to create a vector of class objects?创建 class 对象的向量最优化的是什么?

Depends always on the situation.总是视情况而定。 Mesure, profile and make your decision according to your data.根据您的数据测量、分析并做出决定。

What is the difference between these ways?这些方式有什么区别?

They are stored in different ways.它们以不同的方式存储。

A vector of values look like this in memory: memory 中的值向量如下所示:

+----------------------+
| std::vector<MyClass> |----
+----------------------+   |
                           |
   -------------------------
   |
   v
+-------------+-------------+-------------+-------------+
|   MyClass   |   MyClass   |   MyClass   |   MyClass   |
+-------------+-------------+-------------+-------------+

Whereas a vector of pointer look like this:而指针向量看起来像这样:

+-----------------------+
| std::vector<MyClass*> |---
+-----------------------+  |
                           |
          ------------------
          |
          v
       +-------+-------+-------+-------+
       |  ptr  |  ptr  |  ptr  |  ptr  |
       +-------+-------+-------+-------+
           |       |      |          |
           v       |      v          |
+-------------+    | +-------------+ |
|   MyClass   |    | |   MyClass   | |
+-------------+    | +-------------+ |
                   v                 v
        +-------------+         +-------------+
        |   MyClass   |         |   MyClass   |
        +-------------+         +-------------+

Both have advantages and disadvantages.两者都有优点和缺点。

For the value:对于值:

  • Pro: Contiguous in memory.亲:在 memory 中连续。 There is no pointer chasing and usually really fast to iterate.没有指针追逐,通常迭代速度非常快。
  • Pro: Automatic memory managemen.亲:自动 memory 管理器。 Vector will manage the memory of every values it allocates. Vector 将管理它分配的每个值的 memory。
  • Con: Reference invalidation.缺点:引用失效。 Resizing the vector will invalidate every reference to the objects inside it.调整向量的大小将使对其内部对象的每个引用都无效。
  • Con: In slower to resize with non-trivial objects.缺点:使用非平凡对象调整大小的速度较慢。 Resizing involve moving objects around.调整大小涉及移动对象。 That may be slower with large or non-trivial objects.对于大型或非平凡的对象,这可能会更慢。

For pointer:对于指针:

  • Pro: No reference invalidation.优点:没有参考失效。 The address of the object are managed by you. object的地址由您管理。
  • Pro: Faster reallocation, since it will be the cost of copying pointers around instead of moving objects around.优点:更快的重新分配,因为这将是复制指针而不是移动对象的成本。
  • Con: Slow iteration.缺点:迭代缓慢。 For each element in the vector, the CPU will have to ask the memory for it and cannot use its cache efficiently.对于向量中的每个元素,CPU 将不得不向 memory 询问它,并且不能有效地使用它的缓存。
  • Con: You must use std::unique_ptr to own the memory, and most likely allocate every objects distinctly.缺点您必须使用std::unique_ptr来拥有 memory,并且很可能清楚地分配每个对象。 Allocating a large amount of distinct objects is slow.分配大量不同的对象很慢。

The default choice should be std::vector<MyClass> .默认选择应该是std::vector<MyClass> This is by far the simplest and work for most cases.这是迄今为止最简单的,适用于大多数情况。 Usually when I need references to those objects, I tend to use index in the vector which are stable as long as there are no element removed in the middle.通常当我需要对这些对象的引用时,我倾向于在向量中使用索引,只要中间没有删除任何元素就可以稳定。

I heard pointers are not recommended in C++听说C++不推荐使用指针

You may have misunderstood.你可能误会了。 While there are cases where there are superior alternatives to pointers, as well as cases where pointers can - but shouldn't - be used unnecessarily, there is no general recommendation against pointers regardless of use case.虽然在某些情况下有更好的指针替代方案,以及在某些情况下可以(但不应该)不必要地使用指针,但无论用例如何,都没有针对指针的一般建议。

Naturally, for better performance, I should go with a vector of a pointer of class objects?自然,为了获得更好的性能,我应该使用 go 与 class 对象的指针向量?

Pointers are not magic that improve performance just by existing.指针并不是仅仅通过存在就能提高性能的魔法。 In fact, there is a good possibility that they worsen the performance.事实上,它们很可能会使性能恶化。 Indirecting through a pointer is not free.通过指针间接不是免费的。

Or maybe is it possible to create a vector of reference of class objects?或者是否可以创建 class 对象的参考向量?

It is not possible.这不可能。 The element type of vector, or any other container cannot be reference.向量的元素类型或任何其他容器不能被引用。 std::reference_wrapper can be used instead.可以使用std::reference_wrapper代替。

What is the difference between these ways?这些方式有什么区别?

vector<MyClass> stores MyClass objects in the vector. vector<MyClass>MyClass对象存储在向量中。 vector<MyClass*> stores pointers in the vector. vector<MyClass*>将指针存储在向量中。 vector<MyClass&> violates the requirements of vector and is ill-formed. vector<MyClass&>违反了 vector 的要求并且格式错误。

What is the most optimized to create a vector of class objects?创建 class 对象的向量最优化的是什么?

The most efficient thing to do is nothing.最有效的做法是什么都不做。 Not creating a vector at all will be at least as fast, and potentially faster than creating a vector.根本不创建向量将至少与创建向量一样快,并且可能更快。 Before understanding how to create a vector optimally, you must first understand what you're trying to achieve by creating the vector.在了解如何以最佳方式创建矢量之前,您必须首先了解通过创建矢量要实现的目标。

Consider the ownership and let that guide your decision:考虑所有权并让其指导您的决定:


If the vector owns them then it is best to use如果vector拥有它们,那么最好使用

vector<MyClass> vectorOfObjects;

or或者

vector<std::unique_ptr<MyClass>> vectorOfObjects;

This last one is useful if you must have a pointer (to a polymorphic base), but you still want the vector to own the object.如果您必须有一个指针(指向多态基),最后一个很有用,但您仍然希望vector拥有 object。


If the ownership lies elsewhere, but you wish to change each pointer from time to time, then use如果所有权位于其他地方,但您希望不时更改每个指针,则使用

vector<MyClass*> vectorOfObjects;

But in this case you might want to look into rather using smart pointers:但在这种情况下,您可能需要考虑使用智能指针:

vector<shared_ptr<MyClass>> vectorOfObjects;

Finally, you cannot use vector<MyClass &> vectorOfObjects;最后,您不能使用vector<MyClass &> vectorOfObjects; but you can use std::reference_wrapper :但您可以使用std::reference_wrapper

vector<std::reference_wrapper<MyClass>> vectorOfObjects;

Again this might only be useful if the vector doesn't own the objects.同样,这可能仅在vector不拥有对象时才有用。


As for performance, each time memory is allocated there might be a cost, so using pointers after the objects were created can help, but it is best to start simple and then measure.至于性能,每次分配 memory 可能会有成本,因此在创建对象后使用指针会有所帮助,但最好从简单开始,然后测量。 vector<MyClass> vectorOfObjects; might even perform better.甚至可能表现更好。 You won't know until you measure.在你测量之前你不会知道。 This has been explored in this answer .这已在此答案中进行了探讨。

Naturally, for better performance,当然,为了更好的性能,

This is not that simple to assume.这不是假设那么简单。 Having a vector of pointers mean the actual values can only be reached via 2 pointer redirection - one for the indexing and the next for the actual value access.拥有一个指针向量意味着实际值只能通过 2 个指针重定向来达到 - 一个用于索引,下一个用于实际值访问。 These pointers could be anywhere in the memory.这些指针可以位于 memory 中的任何位置。 So accessing them could result in plenty of cache misses.因此访问它们可能会导致大量缓存未命中。 It is a good idea to keep the objects as such in the vector.将对象保持在向量中是个好主意。 Change that pattern only if one has sufficient evidence (= profile results).仅当有足够的证据(= 配置文件结果)时才更改该模式。

I heard pointers are not recommended in C++ but I don't understand why.我听说在 C++ 中不推荐使用指针,但我不明白为什么。

Pointers are a lot harder to work with than say, regular objects.指针比普通对象更难处理。 It's not that they are completely bad, it's just that there are just so many better options in almost every use case for pointers.并不是说它们完全不好,只是在几乎每个指针用例中都有很多更好的选择。

So for instance, say you have a function parameter you'd like to modify in the caller function.例如,假设您有一个 function 参数要在调用方 function 中修改。 In C, you'd use a pointer:在 C 中,您将使用指针:

void func(int* someMumber) {
    *somenumber = 3; // modifies value in caller
}

In C++, this is replaced with passing by reference:在 C++ 中,这被替换为通过引用传递:

void func(int& someMumber) {
    somenumber = 3; // modifies value in caller
}

This way, there is no messy syntax to deal with, and no fear of getting passed a bad pointer, etc. This should always work no matter what the caller passes us.^1这样,无需处理混乱的语法,也无需担心传递错误的指针等。无论调用者传递给我们什么,这都应该始终有效。^1

Another example is let's say you want dynamically allocated data:另一个例子是假设你想要动态分配的数据:

int* dynamicArray = new int[size];

When you're done with it, you will have to remember to delete it:完成后,您必须记住将其delete

delete [] dynamicArray;

This can get messy, and then you have to keep track of what you did and didn't delete.这可能会变得混乱,然后你必须跟踪你做了什么和没有删除什么。 And then if you don't delete something, that creates a memory leak.然后,如果您不删除某些内容,则会产生 memory 泄漏。

C++ has a much better solution for this: std::vector . C++ 对此有一个更好的解决方案: std::vector Then I can add as many elements to the array the instant I need them:然后,我可以在需要时向数组中添加尽可能多的元素:

std::vector<int> dynamicArray;

// later, when I need to store a value
dynamicArray.push_back(someValue);

No worrying about leaked data, or anything like that.无需担心数据泄露或类似情况。 It will all just deallocate automatically when it's done.完成后,这一切都会自动释放。

Need a pointer anyway for other some reason?出于其他原因需要指针吗? Try a smart pointer :尝试智能指针

std::unique_ptr<int> ptr = new int;

Smart pointers deallocate data automatically without you have to worry about it.智能指针会自动释放数据,您无需担心。 It just makes your life easier.它只会让你的生活更轻松。 Plus, you can convert these to "raw" pointers whenever you need to.另外,您可以在需要时将它们转换为“原始”指针。

So it's not that they are bad, it's just they are made obsolete by other things.所以并不是它们不好,只是它们被其他东西淘汰了。 Thus, they are not recommended.因此,不推荐使用它们。

Naturally, for better performance, I should go with a vector of a pointer of class objects?自然,为了获得更好的性能,我应该使用 go 与 class 对象的指针向量?

The compiler will optimize a lot for you, so there isn't a lot of need to worry about this.编译器会为你优化很多,所以不必担心这个。 On the other hand, using pointers will make things a lot harder on yourself.另一方面,使用指针会让你自己变得更加困难。 You might want to consider one of the above options instead.您可能需要考虑上述选项之一。

Or maybe is it possible to create a vector of reference of class objects?或者是否可以创建 class 对象的参考向量?

Not sure if this is directly possible, but it might be possible with a std::reference_wrapper .不确定这是否直接可行,但可以使用std::reference_wrapper

So my questions are:所以我的问题是:

What is the difference between these ways?这些方式有什么区别?

What is the most optimized to create a vector of class objects?创建 class 对象的向量最优化的是什么?

The biggest difference is that if you push the address of a local variable to your pointer vector, and it goes out of scope, you vector will be filled with garbage.最大的不同是,如果你将一个局部变量的地址推到你的指针向量中,并且它超出了 scope,那么你的向量将被垃圾填充。 To do this properly, you will need to allocate a new object.要正确执行此操作,您需要分配一个新的 object。 Then you will have to delete it.然后你将不得不删除它。 It's just a lot more work.这只是更多的工作。

If you use a vector of references, you will run into the same types of scope problems that using a local variable address will create.如果您使用引用向量,您将遇到与使用局部变量地址相同类型的 scope 问题。

If you store it by value, you won't have to worry about any of this.如果您按价值存储它,您将不必担心任何这些。

Again, don't worry about optimizations.同样,不要担心优化。 The compiler will figure that out for you.编译器会为你解决这个问题。 If you are trying to do this as an optimization, you are just making more work for yourself.如果您尝试将此作为优化,您只是在为自己做更多的工作。


1: Actually, the caller can't pass literals to us, because non-const references can't bind to literals . 1:实际上,调用者不能将字面量传递给我们, 因为非常量引用不能绑定到字面量 But that is beside the point here.但这不是重点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM