2011-07-08 268 views
26

我正在寫一個使用openFrameworks的應用程序,但我的問題並不是只針對oF;相反,這是關於C++向量的一般問題。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

我們可以用一些代碼嗎?有點難以從解釋中回答這個問題。 – MGZero

+0

我們無法弄清楚爲什麼你的代碼不工作,如果你不發佈任何!請添加一個展示您的問題的例子。 – Cameron

+0

重要的代碼可能不是'矢量'的用法,而是'VideoPlayer'類本身。 –

回答

23

你必須知道在C載體什麼++的是,他們必須使用你的類對象的複製經營者能夠將其輸入到載體。如果你在調用析構函數時自動釋放了這些對象的內存分配,這可以解釋你的問題:你的對象被複制到向量中然後被銷燬。

如果你有,你的對象類,朝向分配的緩衝區點的指針,該對象的副本將指向同一個緩衝區(如果你使用默認的複製操作)。如果析構函數釋放緩衝區,那麼當調用複製析構函數時,原始緩衝區將被解除分配,因此您的數據將不再可用。

如果你使用指針,因爲你通過新的控制你的元件的壽命/破壞,以及向量函數只複製指針朝着自己的元素此問題不會發生。

+1

a)然而,潛在的危險的構造:'VideoVideoPlayer * video = newVideoPlayer;'和'push_back'這個局部變量對某個vector。只有'video'在'vector 視頻'使用它(例如,它們在與示例相同的上下文中分配)之前不會離開上下文時,才能保證工作。 b)按值傳遞(代碼失敗)更安全 - 不明白爲什麼「你的對象被複制到'vector' **然後被銷燬。**」被銷燬 - 爲什麼?應該說,這是一個錯誤/沒有複製構造函數的問題。 –

3

vector此外,內部管家使用原始對象的副本 - 如果獲取副本非常昂貴或不可能,那麼最好使用指針。

如果您使vector成員成爲指針,請使用smart pointer來簡化代碼並將泄漏風險降至最低。

也許你的班級沒有做適當的(即深)複製建設/任務?如果是這樣,指針將工作,但不會將對象實例作爲向量成員。

+0

我認爲,這是一個正確的答案,但不是那些upvoted。 –

6

如果您正在使用new爲對象分配內存,那麼您正在將它分配到堆上。在這種情況下,你應該使用指針。然而,在C++中,一般慣例是創建堆棧上的所有對象,並通過這些對象的副本,而不是周圍的傳遞指針在堆上的對象。

爲什麼這更好?這是因爲C++沒有垃圾收集,所以內存堆中的對象將不會被回收,除非你專門delete的對象。但是,堆棧中的對象在離開作用域時總會被銷燬。如果您在堆棧而不是堆上創建對象,則可以將內存泄漏的風險降至最低。

如果你使用棧而不是堆,你需要編寫好的拷貝構造函數和析構函數。嚴重寫入的拷貝構造函數或析構函數可能導致內存泄漏或雙重釋放。

如果您的對象太大而無法高效地複製,那麼可以使用指針。但是,您應該使用引用計數智能指針(C++ 0x auto_ptr或一個Boost庫指針)以避免內存泄漏。

+0

'std :: auto_ptr'是標準的。它將在C++ 0x中被棄用,以支持'std :: unique_ptr'。也會引入'std :: shared_ptr','std :: weak_ptr'等。堆棧分配並不總是可取的,但使用智能指針進行異常安全的堆管理是。最好傳遞'const&'對象以避免複製調用以獲得性能。 – AJG85

12

我的問題很簡單:爲什麼使用指針工作 矢量沒有,當 您將創建對象 與指針的那些 對象的矢量的載體?

的std ::向量是像新和重新分配,分配的,當你試圖把更多的元素比它的當前大小的原始數組。

所以,如果它包含A指針,就好像你在操縱A *的數組。 當它需要調整大小(你在已經填滿當前容量的時候push_back一個元素),它會創建另一個A *數組,並從上一個數組中複製。

如果它包含A對象,那麼就像是在操縱A的數組一樣,所以如果存在自動生成的位置,則A應該是默認構造的。在這種情況下,整個A對象也被拷貝到另一個數組中。

看到區別? 如果您執行一些需要調整內部數組大小的操作,則std :: vector中的A對象可能會更改地址。這就是在std :: vector中包含對象的大多數問題來自哪裏。

一個使用std :: vector而不會出現這種問題的方法是從一開始就分配一個足夠大的數組。 這裏的關鍵字是「容量」。 std :: vector容量是內存緩衝區的實際大小,它將放置對象。因此,要設置容量,您有兩種選擇:

1)將std :: vector的大小設置爲從一開始就構建所有對象,並使用最大數量的對象 - 這將調用每個對象的構造函數。

2)一旦構建了std :: vector(但沒有任何內容),使用它的reserve()函數:它將分配足夠大的緩衝區(您提供了最大的向量大小)。 IT將確定容量。如果你在這個向量中push_back對象或resize()在你在reserve()調用中提供的大小的限制下,它將永遠不會重新定位內部緩衝區,並且你的對象不會改變內存中的位置,從而指向這些對象總是有效的(一些斷言檢查容量的變化從未發生是一個很好的做法)。

+0

'A'不一定是__default__ constructible,btw – RiaD

+3

這是一個很好的答案,特別是當向量由於對象推回而調整大小時,向量中向量中的對象引用可能指向無效區域的註釋 – nurabha

+1

更好,更清晰的回答。 – TREMOR

2

通常我不直接在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 :) 
} 
2

使用矢量的主要思想是存儲在對象一個連續的空間,當使用指針或智能指針時不會發生