C++學習筆記 2
C++學習筆記2
面向物件
含指標的類string
data怎麼存?
用指標不錯,總不能一個個儲存吧
拷貝構造、拷貝複製
建構函式接受的引數是自己這個類的物件(引用),為拷貝構造。
=
過載,引數是自己這個類的物件,為拷貝複製。
String(const String& str); String& operator=(const String& str); inline String::String(const String& str) { m_data = new char[ strlen(str.m_data) + 1 ]; strcpy(m_data, str.m_data); } inline String& String::operator=(const String& str) { if (this == &str) return *this;//檢測自我賦值,一定要 delete[] m_data;//先清空 m_data = new char[ strlen(str.m_data) + 1 ];//分配一樣的空間 strcpy(m_data, str.m_data);//拷貝 return *this; } String s1("hello"); String s2(s1); String s2=s1;//與上一行一個意思,看函式實現也差不多
只要寫的是帶指標的類,就必須完成這兩個函式。對於不帶指標的類complex,不寫也一樣,編譯器會忠實的完全bit複製過來,已經很好了。但是指標不能複製,空間應該重新分配。
淺拷貝就是純bit複製,造成記憶體洩露和別名alias;深拷貝就是把指標指的內容重新建立並賦值。
這裡的建構函式,拿到外部去實現了。
建構函式
String(const char* cstr=0); #include <cstring> inline String::String(const char* cstr) { if (cstr) { m_data = new char[strlen(cstr)+1]; strcpy(m_data, cstr); } else { //未指定初值 m_data = new char[1]; *m_data = '\0'; } } String* p=new String(“hello”)
解構函式
寫法類似建構函式,與類同名,且前方有個 ~
.
~String();
inline
String::~String()
{
delete[] m_data;
}
當這個類的物件死亡時、離開作用域時,會被呼叫(自動)。
有指標的類,要做解構函式處理動態分配的空間(手動)。
用指標動態分配的物件,也要手動delete。
輸出函式
要寫成全域性函式,
#include <iostream> using namespace std; ostream& operator<<(ostream& os, const String& str) { os << str.get_c_str(); return os; } char* get_c_str() const {return m_data;}//寫到類裡面
堆Heap和棧Stack,與new關鍵字
記憶體
棧是存在於一個函式作用域的一塊記憶體空間,用於存放參數、返回地址等。
堆是由作業系統提供的一塊全域性記憶體空間,程式可以動態分配從其中獲得一定的大小。
Complex c1(1,2);
Complex* c2=new Complex(2,3);
棧裡的內容,函式結束後(離開作用域)就會被釋放。會呼叫解構函式。又稱為 auto object
,因為會自動釋放。
如果是 static object
,直到整個程式結束後才會釋放。
如果是 global object
,有點類似static,也是程式結束才會釋放。
堆裡的內容,必須手動釋放。一個new對應一個delete,其生命在delete後結束。如果不釋放,會記憶體洩露,其嚴重性在於:當作用域結束後,指標的生命結束了,作用域之外沒辦法使用指標,對這一塊的記憶體就失去了控制。
new與delete
new
是先分配memory,再呼叫建構函式ctor。可以分為下述步驟:
Complex* pc=new Complex(1,2);
Complex* pc;
void mem=operator new(sizeof(Complex));//operator new其實是一個名字特別的函式,下一層就是malloc。
pc=static_cast<Complex*>(mem);//指標的型別轉換
pc->Complex::Complex(1,2);//建構函式
建構函式是個成員函式,現在是由指標呼叫,因此相當於指標是建構函式裡的 this
,指向記憶體空間的起點。然後呼叫建構函式複製。
delete
是先呼叫解構函式的dtor,再釋放memory。
String* ps = new String("Hello");
delete ps;
String::~String(ps);//釋放字串裡面的動態分配的空間
operator delete(ps);//下一層是free
記憶體塊大小
vc給分配的記憶體空間一定是16B的倍數。
在debug下。會包含很多除錯資訊,以及cookie(用於記錄長度和分配狀況,提供給malloc和free用)。還可能有補充為16B整數倍的padding。
在release下,沒有除錯資訊,別的一樣。
有中括號的new和delete (array new/delete)
要互相搭配,否則會出錯。寫法如下:
m_data = new char[strlen(cstr)+1];
delete[] m_data;
動態分配的陣列中,在資料前會有一個整數記錄陣列容量。(VC)具體問題如下:
刪除整個陣列沒問題,因為大小寫在cookie裡;但是除了第0個元素,後面的元素的沒有呼叫解構函式,這就造成了記憶體洩露。(當然是因為後面類的變數有指標)