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

Ala*_*lan 36 c++ pointers vector

我正在使用openFrameworks编写应用程序,但我的问题不仅仅是针对oF; 相反,它是关于C++向量的一般性问题.

我想创建一个包含另一个类的多个实例的类,但也提供了一个直观的界面来与这些对象进行交互.在内部,我的类使用了类的向量,但是当我尝试使用vector.at()操作对象时,程序将编译但不能正常工作(在我的情况下,它不会显示视频).

// 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();
Run Code Online (Sandbox Code Playgroud)

在某个地方,有人建议我制作一个指向该类对象的指针向量,而不是这些对象本身的向量.我实现了这一点,它确实像一个魅力.

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();
Run Code Online (Sandbox Code Playgroud)

我是动态地为对象分配内存,即 ofVideoPlayer = new ofVideoPlayer;

我的问题很简单:为什么使用指针向量工作,何时会创建一个对象向量与指向这些对象的指针向量?

小智 27

你必须要知道的关于c ++中的向量的是他们必须使用对象类的复制操作符才能将它们输入到向量中.如果在调用析构函数时自动解除分配这些对象中的内存分配,则可以解释您的问题:您的对象被复制到向量中然后被销毁.

如果在对象类中有指向指定缓冲区的指针,则此对象的副本将指向同一缓冲区(如果使用默认复制运算符).如果析构函数释放缓冲区,则在调用复制析构函数时,将释放原始缓冲区,因此您的数据将不再可用.

如果使用指针,则不会发生此问题,因为您通过new/destroy控制元素的生命周期,并且向量函数仅将指针复制到元素.


Kla*_*aim 21

我的问题很简单:为什么使用指针向量工作,何时会创建一个对象向量与指向这些对象的指针向量?

当你尝试推入比当前大小更多的元素时,std :: vector就像一个用new分配的原始数组并重新分配.

所以,如果它包含A指针,就好像你在操纵一个A*数组.当它需要调整大小时(你已经填充了当前容量的push_back一个elettn),它将创建另一个A*数组并从前一个数据中复制.

如果它包含A对象,那么就像你在操作A的数组一样,如果有自动的realocations发生,那么A应该是默认构造的.在这种情况下,整个A对象也会被复制到另一个数组中.

看到不同?如果你做一些需要调整内部数组大小的操作,std :: vector中的A对象可以改变地址.这就是std :: vector中包含对象的大多数问题的来源.

在没有这些问题的情况下使用std :: vector的一种方法是从一开始就分配一个足够大的数组.这里的关键字是"容量".std :: vector容量是内存缓冲区的实际大小,它将放置对象.因此,要设置容量,您有两种选择:

1)在构造上调整std :: vector的大小,从头开始构建所有对象,最大数量的对象 - 将调用每个对象的构造函数.

2)一旦构造了std :: vector(但它没有任何东西),使用它的reserve()函数:它将分配一个足够大的缓冲区(你提供向量的最大大小).IT将设置容量.如果你在这个向量中push_back对象或者在你在reserve()调用中提供的大小限制下调整resize(),它将永远不会重新定位内部缓冲区,你的对象也不会改变内存中的位置,指向那些对象总是有效的(一些断言来检查容量的变化永远不会发生是一种很好的做法).

  • 这是一个很好的答案,尤其是关于当向量调整因向量推回而调整向量时,向量中对象的内存引用如何指向无效区域的注释 (3认同)

Ste*_*end 7

vector 添加和内部管理使用原始对象的副本 - 如果获取副本非常昂贵或不可能,那么最好使用指针。

如果您将vector成员设为指针,请使用智能指针来简化您的代码并将泄漏风险降至最低。

也许您的班级没有进行适当的(即深的)复制构造/分配?如果是这样,指针将起作用,但不能作为向量成员使用对象实例。


Zhe*_*Mao 7

如果要为使用的对象分配内存,则将new其分配给堆.在这种情况下,您应该使用指针.但是,在C++中,约定通常是在堆栈上创建所有对象并传递这些对象的副本,而不是将指针传递给堆上的对象.

为什么这样更好?这是因为C++没有垃圾收集,因此除非特定delete于对象,否则不会回收堆上对象的内存.但是,堆栈上的对象在离开作用域时总是被销毁.如果在堆栈而不是堆上创建对象,则可以最大程度地降低内存泄漏的风险.

如果使用堆栈而不是堆,则需要编写好的复制构造函数和析构函数.写得不好的副本构造函数或析构函数可能会导致内存泄漏或双重释放.

如果对象太大而无法有效复制,则可以使用指针.但是,您应该使用引用计数智能指针(C++ 0x auto_ptr或Boost库指针)来避免内存泄漏.


Nas*_*zta 5

通常我不直接将类存储在std::vector. 原因很简单:您不知道该类是否是派生的。

例如:

在标题中:

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 );
Run Code Online (Sandbox Code Playgroud)

在来源:

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
}
Run Code Online (Sandbox Code Playgroud)

所以我更喜欢使用shared_ptr

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