我正在使用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();
在某个地方,有人建议我制作一个指向该类对象的指针向量,而不是这些对象本身的向量。我实现了这一点,它确实像一个魅力。
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();
我是动态分配对象的内存,即ofVideoPlayer = new ofVideoPlayer;
我的问题很简单:为什么使用指针向量工作,何时会创建一个对象向量与指向这些对象的指针向量?
答案 0 :(得分:26)
你必须要了解c ++中的向量,他们必须使用对象类的复制运算符才能将它们输入到向量中。如果在调用析构函数时自动解除分配这些对象中的内存分配,则可以解释您的问题:您的对象被复制到向量中然后被销毁。
如果在对象类中有指向指定缓冲区的指针,则此对象的副本将指向同一缓冲区(如果使用默认复制运算符)。如果析构函数释放缓冲区,则在调用复制析构函数时,将释放原始缓冲区,因此您的数据将不再可用。
如果使用指针,则不会发生此问题,因为您通过new / destroy控制元素的生命周期,并且向量函数仅将指针复制到元素。
答案 1 :(得分:18)
我的问题很简单:为什么要使用 指针的矢量工作,何时 你会创建一个对象矢量 与指向那些指针的向量 对象
std::vector
就像一个原始数组,当你试图推入比当前大小更多的元素时,用new分配并重新分配。
因此,如果它包含A
指针,就像你在操作A*
数组一样。
当需要调整大小时(当push_back()
元素已经填充到其当前容量时),它将创建另一个A*
数组并从前一个向量中复制A*
数组。
如果它包含A
个对象,那么就像你在操作A
数组一样,如果发生自动重新分配,A
应该是默认构造的。在这种情况下,整个A
对象也会被复制到另一个数组中。
看到区别? 如果您执行一些需要调整内部数组大小的操作,A
中的std::vector<A>
个对象可以更改地址。这就是std::vector
中包含对象的大多数问题来自。
在没有此类问题的情况下使用std::vector
的方法是从一开始就分配足够大的数组。 此处的关键字是“容量”。 std::vector
容量是内存缓冲区的实际大小,用于放置对象。因此,要设置容量,您有两种选择:
1)在构造上调整std::vector
的大小,从头开始构建所有对象,最大数量的对象 - 将调用每个对象的构造函数。
2)构建std::vector
后(但其中没有任何内容),使用其reserve()
函数:向量将分配足够大的缓冲区(您提供矢量的最大尺寸)。 向量将设置容量。如果您在此向量中push_back()
个对象或resize()
在reserve()
调用中提供的大小限制,它将永远不会重新分配内部缓冲区,并且您的对象不会更改位置在内存中,指向这些对象的指针始终有效(一些断言检查容量的变化永远不会发生是一种很好的做法)。
答案 2 :(得分:7)
如果使用new
为对象分配内存,则将其分配给堆。在这种情况下,您应该使用指针。但是,在C ++中,约定通常是在堆栈上创建所有对象并传递这些对象的副本,而不是将指针传递给堆上的对象。
为什么这样更好?这是因为C ++没有垃圾收集,因此除非您特意delete
该对象,否则将无法回收堆上对象的内存。但是,堆栈上的对象在离开作用域时总是被销毁。如果在堆栈而不是堆上创建对象,则可以最大程度地降低内存泄漏的风险。
如果使用堆栈而不是堆,则需要编写好的复制构造函数和析构函数。写得不好的副本构造函数或析构函数可能会导致内存泄漏或双重释放。
如果对象太大而无法有效复制,则可以使用指针。但是,您应该使用引用计数智能指针(C ++ 0x auto_ptr或Boost库指针)来避免内存泄漏。
答案 3 :(得分:4)
vector
添加和内部管家使用原始对象的副本 - 如果复制是非常昂贵或不可能的,那么使用指针是更可取的。
如果您使vector
成员成为指针,请使用smart pointer来简化代码并将泄密风险降至最低。
也许你的班级没有做正确的(即深)复制建设/作业?如果是这样,指针将起作用,但对象实例不起作为矢量成员。
答案 4 :(得分:3)
通常我不会直接在std::vector
中存储课程。原因很简单:你不知道这个类是否派生。
E.g:
标题:
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 );
来源:
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
}
所以我更喜欢使用shared_ptr
:
void work_on_some_class( base &_arg )
{
vector<shared_ptr<base> > store;
...
store.push_back(_arg.clone());
// no issue :)
}
答案 5 :(得分:2)
使用向量的主要思想是在使用不会发生的指针或智能指针时将对象存储在连续空间中