2016-01-07 64 views
-5

我在Visual Studio中編寫了一個需要malloc和free的C++類。malloc和免費構造函數和destrtructor

我使用例如與malloc分配的私有浮點數組。 我的類的一個方法用malloc分配該數組。 我想在釋放那個內存的時候釋放這個內存。

但是當我嘗試釋放我的內存在析構函數中並運行我的程序時,Visual Studio會在free()的行觸發一個斷點。 這是什麼原因?在我的情況下,是否有更好的可能性來分配動態分配和釋放內存?

下面是一些示例代碼:

頁眉文件

class Foo { 
float* a; 

public: 

    Foo(void); 
    ~Foo(void); 

    void Foo::allocateMemory(int size); 
} 

的源文件:

#include "Foo.h" 

Foo::Foo(void){ 
a = NULL; 
} 

Foo::~Foo(void){ 
free(a) 
} 

Foo::allocateMemory(int size){ 
a = (float *)malloc(sizeof(float)*size); 
} 
+2

爲什麼在C++中使用malloc/free而不是new/delete? – John3136

+7

'是否有更好的可能性來分配動態分配和釋放內存?'是的,'std :: vector '。就目前而言,你的班級正在爲0/3/5違規行爲制定多項規則。 – user657267

+0

不要使用'new []'和'delete []',它只會讓事情變得更糟糕。 –

回答

1

答案和評論使用std::vector是正確的錢。請使用std::vector而不是你想要完成的。


話雖如此,讓我們一起玩,看看我們是否可以修復你寫的代碼。

我們首先將mallocfree替換爲new[]delete[]。但是,即使發生這種變化,也會發生同樣的問題,所以我們需要更多。

class Foo 
{ 
    float* a; 

    public: 
     Foo(); 
     ~Foo(); 
     void allocateMemory(int size); 
}; 

Foo::Foo() 
{ 
    a = 0; 
} 

Foo::~Foo() 
{ 
    delete [] a; 
} 

void Foo::allocateMemory(int size) 
{ 
    a = new float[size]; 
} 

從第一眼看上去,一切似乎是確定 - 你在構造函數分配並在析構函數釋放。但是會出現的問題是當您開始在實際程序中使用Foo對象時,並且您試圖複製Foo對象。

例如:

int main() 
{ 
    Foo f1; 
    f1.allocateMemory(10); // ok 
    Foo f2; 
    f2.allocateMemory(20); // ok 
} 

上面應該運行正常。然而,讓我們把在一行代碼:

int main() 
{ 
    Foo f1; 
    f1.allocateMemory(10); // ok 
    Foo f2; 
    f2.allocateMemory(20); // ok 
    f1 = f2;  // trouble is going to happen, but not yet 
} // now it happens 

一條線f1 = f2;就是麻煩的開始位置。當您將對象分配給另一個對象時,默認情況下,所有成員都將執行淺拷貝。這意味着f1::af2::a將在分配完成後指向相同的內存。

當在f2上調用析構函數時,則完成delete [] a;。但是,如果在f1上調用析構函數,它也將調用delete [] a,其中a指向與f2::a指向的內存相同的內存。結果 - 雙倍空閒錯誤和未定義的行爲(可能崩潰),因爲在同一個內存塊上調用delete []兩次。

Crashing example

所以你需要被調用時賦值(和拷貝構造)以某種方式複製數據,而不是指針值,對象之間。 f1f2都需要單獨的內存區域,以便其各自的a成員指向(內存區域具有相同的數據),以便在爲每個對象調用析構函數時,會破壞不同的內存區域。

這是C++中rule of 3的一部分。基本上,如果你的類有一個析構函數,那麼它很可能必須有一個拷貝構造函數和賦值運算符。

所以固定的Foo類,它應該是這個樣子的時候添加拷貝構造函數和賦值操作符:

class Foo 
{ 
    float* a; 
    int aSize; 

    public: 
     Foo(const Foo& f); // copy constructor 
     Foo& operator=(const Foo& f); // assignment operator 
     Foo(); 
     ~Foo(); 
     void allocateMemory(int size); 
}; 

和實現:

#include <algorithm> 
#include <cstriing> 
#include "foo.h" 

Foo::Foo() :a(0), aSize(0) // use member initialization list -- a good habit 
{ 
} 

Foo::~Foo() 
{ 
    delete [] a; 
} 

void Foo::allocateMemory(int size) 
{ 
    a = new float[size]; 
    aSize = size; 
} 

Foo::Foo(const Foo& rhs) : a(0) 
{ 
    allocateMemory(rhs.aSize); // allocate memory 
    memcpy(a, rhs.a, aSize * sizeof(float)); // copy the data from rhs 
} 

Foo& Foo::operator=(const Foo& rhs) 
{ 
    Foo temp(rhs); // copy construct 
    std::swap(a, temp.a); 
    std::swap(aSize, temp.aSize); 
    return *this; 
} 

注意,有一個附加的aSize會員。爲了有效地使用你的類,你必須保留分配的浮點數的某個地方。這對於複製正常工作是必需的。此外,使用new[]delete[]代替mallocfree(儘管無論如何,無論您使用的是new[]/delete[]還是malloc/free,都必須進行相同的更改。

賦值運算符(operator=)使用copy/swap idiom,該函數使用複製構造函數和析構函數作爲助手,以正確書寫賦值運算符。

Working example

所以你看你需要做的,使你的類不會崩潰時的所有工作?這就是爲什麼你應該使用std::vector - 所有這些工作以及其他功能都已經爲你完成了。

3

一種更好的方式來管理,這是使用std::vector,這正是設計對於這個用例。對於大多數編譯器來說,使用向量而不是原始指針將沒有開銷,並且意味着你不會遇到原始指針帶來的所有內存管理和安全問題。要使用它,你需要是一個矢量更換您float *成員:

#include <vector> 

class Foo { 
    std::vector<float> a; 
}; 

你的整個allocateMemory功能然後可以通過簡化您的呼叫a.resize(size)所取代。由於RAII的奇蹟,當您的Foo對象被破壞時,a的內存將自動,安全且一致地釋放。

其他一些一般性意見:你似乎很編寫類似C的代碼,而不是使用C++成語:

  • newdelete應使用,而不是mallocfree
  • 您不需要將void置於空參數列表中。
  • 應該使用nullptr而不是NULL
  • 您幾乎從不需要使用newdelete或手動指針管理。查看std::unique_ptrstd::shared_ptr來管理內存。 C++ 11和STL有許多非常有用的工具,可以消除手動內存和資源管理帶來的痛苦。使用它們!

這可能是值得檢查出像香草薩特的Elements of Modern C++ Style寫現代C++,而不是試圖堅持下用扔在上面的幾個額外位的advantadges的概述。

+0

感謝您的幫助! – user123