2009-04-27 49 views
1

比方說,我有一個名爲頂點與添加了兩個頂點的方法結構:何時可以安全地將對象值添加到堆中的向量上?

struct vertex { 
    float x, y, z; 

    // constructs the vertex with initial values 
    vertex(float ix, float iy, float iz); 

    // returns the value c = this + b 
    vertex operator+(vertex b); 
}; 

vertex::vertex(float ix, float iy, float iz){ 
    this->x = ix; this->y = iy; this->z = iz; 
} 

vertex vertex::operator+(vertex b){ 
    vertex c; 
    c.x = this->x + b.x; 
    c.y = this->y + b.y; 
    c.z = this->z + b.z; 
    return c; 
} 

在另一個調用函數我想補充兩個頂點在一起,結果添加到vector<vertex*>。何時可以安全地使用返回的值添加到給定的向量?如果從不,我將如何實現它?

例如,

vector<vertex*> v; 
vertex a(1, 2, 3); 
vertex b(4, 5, 6); 
v.push_back(&(a + b)); 

回答

2

這不是安全的,因爲要存儲的指針到自動或臨時變量,噹噹前函數終止,這將被回收。

將動態分配的對象與自動分配的對象混合在一起存在着嚴重的風險。有時最好的策略是完全禁止自動分配(例如,通過將構造函數設爲私有,並使用工廠方法創建新實例)。然後你會負責在某些時候消除這些。

第二個選項(不一定是你想要的)是按價值做所有事情。有一個Vertex矢量而不是Vertex *,並且只有在存儲頂點時纔會複製頂點。你的班級寫作的方式,所有的領域都是原始的,所以這可能足夠好,你不會遇到性能或深層複製語義問題。

+0

這澄清了一點,但鑑於此,如果不從操作符函數返回指針,這是一種安全的方法嗎?我想能夠鏈接不同的操作員 - 即a + b + c。 – Asuah 2009-04-27 01:03:42

1

它永遠不會保存,因爲您將一個指向臨時對象的指針添加到該向量中。該行被執行後,該臨時對象將被銷燬,並留下帶有無效指針的向量。

你有兩種可能性。要麼你不存儲在矢量指針和使用vector<vertex>代替,或者如果你把你明確分配臨時對象的新副本:

v.push_back(new vertex(a+b)); 
1

另一種方法是插入智能指針變成STD容器,比如boost:shared_ptr,因爲共享指針會爲你處理內存管理。但是,您需要從Shared_Ptr vertex :: operator +(vertex b)返回一個共享指針,這意味着返回值仍然在堆上。

如果你對boost:shared_ptr不熟悉,那麼就按照其他帖子所建議的那樣按照值處理所有內容。這實際上是標準的做法。

1

這是不安全的,因爲您要添加到矢量的頂點值是而不是在堆上。返回的頂點對象位於堆棧中,所以一旦當前函數終止,它們的內存位置可能會被覆蓋。如果在當前函數結束後該向量(或其副本)仍然存在,則不能保證其指針仍將引用有效的頂點對象。

這種情況中最危險的部分是那些頂點對象可能在創建它們的函數結束後很長時間內在內存中存活。我曾經看到過一個例子,一個學生在一個非常長的構造函數中用一個指向值對象的向量來填充向量。該向量是該對象中的成員字段,因此在構造函數結束後它仍然存在。由於構造函數很長,矢量指向的值對象直到通過不同函數的中途才被覆蓋。在調試器中觀察矢量的內容時,會產生令人困惑的錯覺,即對象在內存中自發破壞。

除非確實需要將這些頂點存儲爲指針,否則最安全的策略是將它們作爲值對象存儲在向量中:vector<vertex>而不是vector<vertex*>。您仍然可以將不同的運營商捆綁在一起;事實上,將它們作爲值對象一起更容易,因爲您不會經常需要取消引用指針。

相關問題