简体   繁体   English

C++ 对象向量与对象指针向量

[英]C++ vector of objects vs. vector of pointers to objects

I am writing an application using openFrameworks, but my question is not specific to just oF;我正在使用 openFrameworks 编写一个应用程序,但我的问题不仅仅针对 oF; rather, it is a general question about C++ vectors in general.相反,这是一个关于 C++ 向量的一般性问题。

I wanted to create a class that contains multiple instances of another class, but also provides an intuitive interface for interacting with those objects.我想创建一个 class,它包含另一个 class 的多个实例,但也提供了一个用于与这些对象交互的直观界面。 Internally, my class used a vector of the class, but when I tried to manipulate an object using vector.at(), the program would compile but not work properly (in my case, it would not display a video).在内部,我的 class 使用了 class 的向量,但是当我尝试使用 vector.at() 操作 object 时,程序会编译但无法正常工作(在我的例子中,它不会显示视频)。

// instantiate object dynamically, do something, then append to vector
vector<ofVideoPlayer> videos;
ofVideoPlayer *video = new ofVideoPlayer;
video->loadMovie(filename);
videos.push_back(*video);

// access object in vector and do something; compiles but does not work properly
// without going into specific openFrameworks details, the problem was that the video would
// not draw to screen
videos.at(0)->draw();

Somewhere, it was suggested that I make a vector of pointers to objects of that class instead of a vector of those objects themselves.在某处,有人建议我制作一个指向 class 的对象的指针向量,而不是这些对象本身的向量。 I implemented this and indeed it worked like a charm.我实现了这一点,确实它很有魅力。

vector<ofVideoPlayer*> videos;
ofVideoPlayer * video = new ofVideoPlayer;
video->loadMovie(filename);
videos.push_back(video);
// now dereference pointer to object and call draw
videos.at(0)->draw();

I was allocating memory for the objects dynamically, ie ofVideoPlayer = new ofVideoPlayer;我正在为对象动态分配 memory,即ofVideoPlayer = new ofVideoPlayer;

My question is simple: why did using a vector of pointers work, and when would you create a vector of objects versus a vector of pointers to those objects?我的问题很简单:为什么使用指针向量有效,什么时候创建对象向量而不是指向这些对象的指针向量?

What you have to know about vectors in c++ is that they have to use the copy operator of the class of your objects to be able to enter them into the vector.关于 c++ 中的向量,您必须了解的是,它们必须使用对象的 class 的复制运算符才能将它们输入向量中。 If you had memory allocation in these objects that was automatically deallocated when the destructor was called, that could explain your problems: your object was copied into the vector then destroyed.如果您在这些对象中有 memory 分配在调用析构函数时自动释放,这可以解释您的问题:您的 object 被复制到向量中然后被销毁。

If you have, in your object class, a pointer that points towards a buffer allocated, a copy of this object will point towards the same buffer (if you use the default copy operator).如果在 object class 中有一个指向已分配缓冲区的指针,则此 object 的副本将指向同一缓冲区(如果您使用默认复制运算符)。 If the destructor deallocates the buffer, when the copy destructor will be called, the original buffer will be deallocated, therefore your data won't be available anymore.如果析构函数释放缓冲区,当调用复制析构函数时,原始缓冲区将被释放,因此您的数据将不再可用。

This problem doesn't happen if you use pointers, because you control the life of your elements via new/destroy, and the vector functions only copy pointer towards your elements.如果您使用指针,则不会发生此问题,因为您通过 new/destroy 控制元素的生命周期,并且矢量函数仅将指针复制到您的元素。

My question is simple: why did using a vector of pointers work, and when would you create a vector of objects versus a vector of pointers to those objects?我的问题很简单:为什么使用指针向量有效,什么时候创建对象向量而不是指向这些对象的指针向量?

std::vector is like a raw array allocated with new and reallocated when you try to push in more elements than its current size. std::vector就像一个原始数组,当您尝试推入比其当前大小更多的元素时,分配了新的并重新分配。

So, if it contains A pointers, it's like if you were manipulating an array of A* .所以,如果它包含A指针,就好像你在操作一个A*数组。 When it needs to resize (you push_back() an element while it's already filled to its current capacity), it will create another A* array and copy in the array of A* from the previous vector.当它需要调整大小时(你push_back()一个元素已经填充到它的当前容量),它会创建另一个A*数组并从前一个向量复制到A* * 数组中。

If it contains A objects, then it's like you were manipulating an array of A , so A should be default-constructible if there are automatic reallocations occuring.如果它包含A对象,那么就像您在操作A数组一样,因此如果发生自动重新分配, A应该是默认可构造的。 In this case, the whole A objects get copied too in another array.在这种情况下,整个A对象也会被复制到另一个数组中。

See the difference?看到不同? The A objects in std::vector<A> can change address if you do some manipulations that requires the resizing of the internal array.如果您进行一些需要调整内部数组大小的操作,则std::vector<A>中的A对象可以更改地址。 That's where most problems with containing objects in std::vector comes from.这就是在std::vector中包含对象的大多数问题的来源。

A way to use std::vector without having such problems is to allocate a large enough array from the start.在没有此类问题的情况下使用std::vector的一种方法是从一开始就分配一个足够大的数组。 The keyword here is "capacity".这里的关键词是“容量”。 The std::vector capacity is the real size of the memory buffer in which it will put the objects. std::vector容量是 memory 缓冲区的实际大小,它将在其中放置对象。 So, to setup the capacity, you have two choices:因此,要设置容量,您有两种选择:

1) size your std::vector on construction to build all the object from the start, with maximum number of objects - that will call constructors of each objects. 1)在构造时调整您的std::vector大小,以从一开始就构建所有 object,并使用最大数量的对象 - 这将调用每个对象的构造函数。

2) once the std::vector is constructed (but has nothing in it), use its reserve() function : the vector will then allocate a large enough buffer (you provide the maximum size of the vector). 2)一旦构造了std::vector (但其中没有任何内容),请使用它的reserve() function :然后向量将分配足够大的缓冲区(您提供向量的最大大小)。 The vector will set the capacity.向量将设置容量。 If you push_back() objects in this vector or resize() under the limit of the size you've provided in the reserve() call, it will never reallocate the internal buffer and your objects will not change location in memory, making pointers to those objects always valid (some assertions to check that change of capacity never occurs is an excellent practice).如果您在此向量中push_back()对象或resize()在您在reserve()调用中提供的大小限制下,它将永远不会重新分配内部缓冲区,并且您的对象不会更改 memory 中的位置,从而指向这些对象总是有效的(一些检查容量变化从未发生的断言是一种很好的做法)。

If you are allocating memory for the objects using new , you are allocating it on the heap.如果您使用new为对象分配 memory ,那么您就是在堆上分配它。 In this case, you should use pointers.在这种情况下,您应该使用指针。 However, in C++, the convention is generally to create all objects on the stack and pass copies of those objects around instead of passing pointers to objects on the heap.但是,在 C++ 中,约定通常是在堆栈上创建所有对象并传递这些对象的副本,而不是传递指向堆上对象的指针。

Why is this better?为什么这样更好? It is because C++ does not have garbage collection, so memory for objects on the heap will not be reclaimed unless you specifically delete the object.因为C++没有垃圾回收,所以堆上的对象的memory不会被回收,除非你特意delete object。 However, objects on the stack are always destroyed when they leave scope.但是,堆栈上的对象总是在离开 scope 时被销毁。 If you create objects on the stack instead of the heap, you minimize your risk of memory leaks.如果您在堆栈而不是堆上创建对象,则可以最大限度地降低 memory 泄漏的风险。

If you do use the stack instead of the heap, you will need to write good copy constructors and destructors.如果您确实使用堆栈而不是堆,则需要编写良好的复制构造函数和析构函数。 Badly written copy constructors or destructors can lead to either memory leaks or double frees.编写不当的复制构造函数或析构函数可能导致 memory 泄漏或双重释放。

If your objects are too large to be efficiently copied, then it is acceptable to use pointers.如果您的对象太大而无法有效复制,那么使用指针是可以接受的。 However, you should use reference-counting smart pointers (either the C++0x auto_ptr or one the Boost library pointers) to avoid memory leaks.但是,您应该使用引用计数智能指针(C++0x auto_ptr 或 Boost 库指针之一)来避免 memory 泄漏。

vector addition and internal housekeeping use copies of the original object - if taking a copy is very expensive or impossible, then using a pointer is preferable. vector加法和内部管理使用原始 object 的副本 - 如果获取副本非常昂贵或不可能,则最好使用指针。

If you make the vector member a pointer, use a smart pointer to simplify your code and minimize the risk of leaks.如果将vector成员设为指针,请使用智能指针来简化代码并将泄漏风险降至最低。

Maybe your class does not do proper (ie. deep) copy construction/assignment?也许您的 class 没有正确(即深)复制构造/分配? If so, pointers would work but not object instances as the vector member.如果是这样,指针将起作用,但 object 实例不能作为向量成员。

Usually I don't store classes directly in std::vector .通常我不会将类直接存储在std::vector中。 The reason is simple: you would not know if the class is derived or not.原因很简单:您不会知道 class 是否派生。

Eg:例如:

In headers:在标题中:

class base
{
public:
  virtual base * clone() { new base(*this); };
  virtual ~base(){};
};
class derived : public base
{
public:
  virtual base * clone() { new derived(*this); };
};
void some_code(void);
void work_on_some_class( base &_arg );

In source:在来源:

void some_code(void)
{
  ...
  derived instance;
  work_on_some_class(derived instance);
  ...
}

void work_on_some_class( base &_arg )
{
  vector<base> store;
  ...
  store.push_back(*_arg.clone());
  // Issue!
  // get derived * from clone -> the size of the object would greater than size of base
}

So I prefer to use shared_ptr :所以我更喜欢使用shared_ptr

void work_on_some_class( base &_arg )
{
  vector<shared_ptr<base> > store;
  ...
  store.push_back(_arg.clone());
  // no issue :)
}

The main idea of using vector is to store objects in a continue space, when using pointer or smart pointer that won't happen使用向量的主要思想是将对象存储在连续空间中,当使用不会发生的指针或智能指针时

Here also need to keep in mind the performance of memory usage by CPU.这里还需要记住 memory CPU 使用率的性能。

  • std::vector vector guarantees(not sure) that the mem block is continuous. std::vector 向量保证(不确定)内存块是连续的。
  • std::vectorstd::unique_ptr<Object> will keep smart-pointers in continuous memory, but real memory blocks for objects can be placed in different positions in RAM. std::vectorstd::unique_ptr<Object> 将保持智能指针连续 memory,但对象的实际 memory 块可以放置在 RAM 中的不同位置。

So I can guess that std::vector will be faster for cases when the size of the vector is reserved and known.所以我可以猜测 std::vector 在向量大小被保留和已知的情况下会更快。 However, std::vectorstd::unique_ptr<Object> will be faster if we don't know the planned size or we have plans to change the order of objects.但是,如果我们不知道计划的大小或者我们计划更改对象的顺序,std::vectorstd::unique_ptr<Object> 会更快。

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

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