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 }
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
注意的點
- 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