2017-12-18 150 views
0

自大學以來沒有使用C++後,我試圖使用具有兩種類型的子對象的向量,而且我顯然得到了一些錯誤。包含多態對象的向量:靜態斷言錯誤

最初我使用了一個可以工作的指針向量,但是如果我理解正確,那麼在清除時會泄漏內存。

我得到的錯誤導致我相信它與類中的靜態計數器有關(與最後一個成員一起銷燬?),但刪除並沒有解決它。

錯誤會導致在此,在stl_construct.h:

#if __cplusplus >= 201103L 
     // A deleted destructor is trivial, this ensures we reject such types: 
     static_assert(is_destructible<_Value_type>::value, 
      "value type is destructible"); 
#endif 

嗯,好吧,但我的析構函數都明確聲明。我記得父類應該使用虛擬析構函數並修復它,但問題依然存在。

將構造函數/析構函數移到公共虛擬父類上不應該(事實上也沒有)改變它。

現在我假設我以某種方式濫用向量。這是我的例子:

主:

#include <stdio.h> 
#include "Foo.h" 
#include <iostream> 
Bar buildBar(); 

int main(int argc, char **argv) 
{ 
    std::vector<Foo> Foos; 
    Foos.reserve(1); 
    Foos.push_back(buildBar()); 
    for (int idx=(0);sizeof(Foos);idx++) 
    { 
     try { 
      std::cout << "x = " << Foos.at(idx).getLoc().at(0); 
     } 
     catch (std::exception& e) { 
       std::cout << idx << ": Index out of range" << std::endl; 
      } 
    } 
Foos.clear(); 
return 0; 
} 

Bar buildBar() 
{ 
    Bar* temp = new Bar(5,6); 
    return *temp; 
} 

foo.h中,用構造移到頭:

#ifndef FOO_H 
#define FOO_H 
#include <vector> 
class Foo 
{ 
public: 
    //int maxoID(){return lastoID;} //0-indexed 
    /* Other things */ 
    virtual std::vector<int> getLoc(){ return {x_Base,y_Base};} 
    Foo(): 
    //oID(-1), 
    x_Base(-1), 
    y_Base(-1){} 
    virtual ~Foo(){} 
protected: 
    int x_Base; 
    int y_Base; 
}; 

class Bar : public Foo 
{ 
public: 
    Bar(int x1, int y1): 
     x_End(-1), 
     y_End(-1) 
     { 
      x_Base = x1; 
      y_Base = y1; 
     } 
    ~Bar(){} 
    std::vector<int> getLoc() {return {x_Base,y_Base,x_End,y_End};} 
protected: 
    int x_End; 
    int y_End; 

}; 

#endif // FOO_H 
+1

你'buildBar()'函數是內存泄漏的向量提取的元素。 – juanchopanza

+3

「_Initially我使用了一個向量指針,它的工作,但如果我理解正確,這將在清除時泄漏內存。」所以,爲什麼不使用'std :: unique_ptr'的std :: vector?相關閱讀:[什麼是對象切片?](https://stackoverflow.com/questions/274626/what-is-object-slicing) –

+2

'sizeof(Foos)'不會告訴你向量中有多少項。 (它會告訴你它佔用了多少字節,但不包含它所包含的數據)。 – Galik

回答

1

首先,良好的對你進行訊問的原始指針的使用!人們往往盲目地使用它們,並最終導致其他問題。

您現在的問題是你有object slicing。當您將Bar插入vector<Foo>時,您將失去有關Bar的重要信息。如果您調用只需要一個Foo而不是Foo&Foo*的函數,則會發生同樣的情況。

根據您的使用,您可以使用std::unique_ptrstd::shared_ptr,或std::reference_wrapper

請注意,您可以使用原始指針,他們將不只是自動內存泄漏,但vector概不負責用於存儲器(這是使用智能指針我指出之一的美容)

這是完全可以接受的:

int main() 
{ 
    Foo* f = new Foo; 
    { 
     std::vector<Foo*> v; 
     v.push_back(f); 
    } // v goes out of scope and is cleaned up 
    delete f; // the vector won't have cleaned this up, it's our responsibility  
} 

使用unique_ptr取而代之只是使這一整件事情變得更加簡單。當然,在這樣的光例如原始指針是易於使用,但在較大的程序能失控:

還要注意,並作爲@juanchopanza points out in the comments,你buildBar函數泄漏內存。當你調用

return *temp; 

您創建一個副本,你會失去temp的內存地址,因此無法將其刪除。

Bar buildBar() 
{ 
    Bar* temp = new Bar(5,6); 
    return *temp; 
} 

Bar b = buildBar(); 
Bar* p = &b; // this only reference the copy, b 
delete p; // this is bad, this will (double) delete the memory from b, which is not the same as temp. 

b將被清理時自動超出範圍(這是壞的,如果你還試圖將其刪除),但你沒有辦法刪除temp

0

歡迎回來到C++!

您不能將派生類型放入基類型的向量中。這些實例只能是基本類型。如果您嘗試向此向量添加派生類型,它將只複製派生類的基礎部分。

你第一次是對的 - 你需要一個基本指針向量。只要您從矢量中刪除指針,就不會泄漏。

但是,這是2017年,我們現在喜歡避免一般的新/刪除。所以你可以使用unique_ptr(C++'11)的向量,它在超出範圍時自動刪除內存。

std::vector<std::unique_ptr<Bar>> v; 
v.push_back(std::make_unique<Bar>(0, 0)); 

你也可以使用變種的向量(C++'17),它們是新型的類型安全聯合。在元素上

typedef std::variant<Bar, Foo> MyBar 
std::vector<MyBar> v; 

使用std::visitstd::get得到它的類型回來。

編輯:下面是一些代碼從變體

bool apply(int& i) 
{ 
    std::cout << "integer: " << i << std::endl; 
    return true; 
} 

bool apply(double& d) 
{ 
    std::cout << "double: " << d << std::endl; 
    return true; 
} 

void test() 
{ 
    typedef std::variant<int, double> asdf; 

    std::vector<asdf> v; 

    v.push_back(10); 
    v.push_back(0.5); 

    auto myLambda = [](auto&& arg) { return apply(arg); }; // use of auto (the 2nd one) is required. 

    for (auto& elem : v) 
    { 
     std::visit(myLambda, elem); 
    } 
} 
+0

謝謝。我現在正在閱讀這個變體。它是否允許我直接訪問子類的公共函數,而不必在父類中聲明它們是虛擬的還是重鑄?由於這是我自己使用的,所以對應用於錯誤類的操作拋出異常是可以接受的,甚至是可取的。 – MGreene

+0

是的。由於您沒有使用指針,因此編譯器可以在編譯時確定函數鏈接,並且根本不使用動態綁定。事實上,這些類型不必是相關的。 –

+0

我添加了一個如何使用變體向量中的元素的例子。 –