[c++] 面向物件課程(二)-- 帶指標類的設計
阿新 • • 發佈:2021-07-29
class with pointer menbers
string_test.cpp
1 #include "string.h" 2 #include <iostream> 3 4 using namespace std; 5 6 int main() 7 { 8 String s1("hello"); 9 String s2("world"); 10 11 String s3(s2); 12 cout << s3 << endl; 13 14 s3 = s1; 15 cout << s3 << endl;View Code16 cout << s2 << endl; 17 cout << s1 << endl; 18 }
string.h
1 #ifndef __MYSTRING__ 2 #define __MYSTRING__ 3 4 class String 5 { 6 public: 7 String(const char* cstr=0); 8 String(const String& str);View Code9 String& operator=(const String& str); 10 ~String(); 11 char* get_c_str() const { return m_data; } 12 private: 13 char* m_data; 14 }; 15 16 #include <cstring> 17 18 inline 19 String::String(const char* cstr) 20 { 21 if (cstr) {22 m_data = new char[strlen(cstr)+1]; 23 strcpy(m_data, cstr); 24 } 25 else { 26 m_data = new char[1]; 27 *m_data = '\0'; 28 } 29 } 30 31 inline 32 String::~String() 33 { 34 delete[] m_data; 35 } 36 37 inline 38 String& String::operator=(const String& str) 39 { 40 if (this == &str) 41 return *this; 42 43 delete[] m_data; 44 m_data = new char[ strlen(str.m_data) + 1 ]; 45 strcpy(m_data, str.m_data); 46 return *this; 47 } 48 49 inline 50 String::String(const String& str) 51 { 52 m_data = new char[ strlen(str.m_data) + 1 ]; 53 strcpy(m_data, str.m_data); 54 } 55 56 #include <iostream> 57 using namespace std; 58 59 ostream& operator<<(ostream& os, const String& str) 60 { 61 os << str.get_c_str(); 62 return os; 63 } 64 65 #endif
注意的點
- string_test.cpp:11拷貝構造,14拷貝賦值
- string.h:8拷貝構造(建構函式接受自己),9拷貝賦值,10解構函式
- 不知道將來要建立的物件多大,所以只放一根指標,再動態建立空間放物件
- 類中帶指標,要關注三個特殊函式(Big Three)
- 結束符號“\0”判斷字串結束
- 21判斷是否空指標
- 22:new:,動態分配一塊記憶體
- 31-35:delete,解構函式,防止記憶體洩露,變數離開作用域時自動呼叫
- 11的get_c_str()是為了配合輸出函式
拷貝構造(copy ctor)
- String a("Hello");String b("World");b = a;
- 如果沒有構造拷貝,會發生b和a的指標都指向“Hello”,“World”沒有指標指向,導致記憶體洩露
- 而且a、b一個改動會影響另一個,別名在程式設計中是危險的事
- 這種拷貝方式稱為“淺拷貝”,是編譯器的預設版本
- copy ctor 實現“深拷貝”,建立足夠的新空間存放藍本
- String s2(s1); 與 String s2 = s1; 效果相同
拷貝賦值(copy op =)
- String s1("hello"); String s2(s1); s2 = s1;
- 過程:43刪除左值--44開闢新空間--45拷貝右值
- 40-41:檢測自我賦值(self assignment),來源端和目的端是否相同
- 這步的意義不只是提高效率,不這樣寫會出錯
- 因為43會把原空間刪掉,導致45行復制的時候訪問空指標
棧(stack)和堆(heap)
- {Complex c1(1,2);}
- {Complex* p = new Complex(3);
- delete p;}
- stack:存在於某作用域(scope)的一塊記憶體空間,呼叫函式時,函式本身即形成一個stack用來防止它接受的引數,以及返回地址,離開作用域後,解構函式被自動呼叫(aoto object)
- heap:system heap,作業系統提供的一塊global記憶體空間,程式可以動態分配(dynamic allocated)從中獲得若干區域,使用完後需手動釋放空間
- {static Complex c1(1,2);}
- 靜態物件,離開作用域後物件仍存在,組用域是整個程式
- 全域性物件,寫在任何作用域之外(全域性作用域之中),也可看做一種靜態物件
- new:先分配memory,再呼叫ctor,分解為以下三個函式
- void* mem = operator new(sizeof(Complex)); // 分配記憶體,operator new呼叫malloc(n)
- p = static_cast<Complex*>(mem); // 轉型
- p->Complex::Complex(1,2); // 建構函式,Complex::Complex(pc,1,2);
- delete:先呼叫解構函式,再釋放記憶體
- String::~String(p); // 把字串裡面動態分配的記憶體刪掉(字串本身只是指標m_data)
- operator delete(p); // 內部呼叫free(p),刪掉字串本身的指標
new到底分配多少記憶體
- 除錯模式下加上debug header 32byte,正常模式下中間為資料(一根指標4byte),上下為cookies 2*4byte,湊夠16的倍數
- 動態分配所得的array
- new []:array new
- delete[]:array delete
- array new 一定要搭配 array delete
- String* p = new String[3]; delete[] p; // 喚起3次dtor
- String* p = new String[3]; delete p; // 喚起1次dtor,另外2個指標指向的動態記憶體資料無法刪除(注意指標是可以刪除的)
- 雖然new complex的時候不會產生此問題,但也要注意搭配使用,養成好習慣
參考:
const在函式前面與後面的區別