1. 程式人生 > 其它 >[c++] 面向物件課程(二)-- 帶指標類的設計

[c++] 面向物件課程(二)-- 帶指標類的設計

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;     
16 cout << s2 << endl; 17 cout << s1 << endl; 18 }
View Code

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);                    
9 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
View Code

注意的點

  • 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在函式前面與後面的區別

https://blog.csdn.net/qq_25800311/article/details/83054129