標籤:64bit 操作 位元組 debug 解構函式 全域 oid 管理 color
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
接下來的幾篇文章,我將回憶一下C++的基礎.
C++的由兩部分組成 1.C++語言 2.C++標準庫 本篇文章主要分享我學習C++語言的筆記.
本節主要介紹 Big Three 即解構函式,拷貝建構函式,賦值拷貝函數,前面主要圍繞不帶指標的class complex本節中主要圍繞帶指標的String類
前面我說過如果你建立的類不帶有指標,那麼多半你可以不用寫解構函式,但是如果你建立了一個帶指標的類,那麼你必須重寫Big Three
建立一個類
class String{public: String(const char* cstr=0); String(const String& str); String& operator=(const String& str); ~String(); char* get_c_str() const { return m_data; }private: char* m_data; //由於帶有指標 ,所以需要重寫解構函式,拷貝構造,賦值拷貝};
一.拷貝構造
如下操作會調用拷貝建構函式
String a{"hello"};
它的實現為
inlineString::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 a{"hello"};String b{"world"};s1 = s2;
若果我們使用編譯器內建的賦值拷貝就會發生下面的現象
這樣操作會產生野指標, 因為a和b同時指向 hello ,沒人指向world
同時如果a和b同時指向同一塊記憶體,如果你刪掉a的話 b指向的記憶體也會被刪掉,這可不是我們想要的
所以我們必須重寫賦值拷貝函數,下面是正確的賦值拷貝函數
inlineString& String::operator=(const String& str) //拷貝賦值{ if (this == &str) //防止自我賦值 //如果不寫這個判斷,那麼執行帶1的時候,就會先殺掉自己,導致錯誤 return *this; delete[] m_data; //1釋放自己記憶體 m_data = new char[ strlen(str.m_data) + 1 ];//2.建立新的記憶體 strcpy(m_data, str.m_data); //3.copy return *this;}
賦值拷貝的三個步驟: 1.釋放自身記憶體 2.建立新記憶體 3.copy
注意上面的紅色部分
三.重寫操作符
因為string類是你新建立的,所有cout不識別你自己建立的類,所以你要重寫一個<<
#include <iostream>using namespace std;ostream& operator<<(ostream& os, const String& str) //如果寫成成員函數調用的時候是這樣 c1 << cout 是不是很難接受啊{ os << str.get_c_str(); return os;}
四.生命週期
下面我來介紹一個記憶體管理
1.Stack(棧),是存在於某範圍 (scope) 的一一塊記憶體空間 (memory space)。例如當你調用函數,函數本身即 會形成一個 stack 用來放置它所接的參數,以及返回地址
2.Heap, system heap,是指由操作系統提供的 一塊 global 內存空間,程式可動態分配 (dynamic allocated) 從某中獲得若干區塊 (blocks)
例如
String s1{"hello"};//stackString s2 = new String("world");// 動態分配 heapstatic Complex c2(1,2); //static
stack 棧 的生命週期在範圍結束之際結束.自動清理
heap 堆 的生命週期在他被調用delete之際結束
static 靜態對象 生命週期會一直存在帶程式結束之際
global 全域對象 其生命在整個程式結束之後 才結束。你也可以把它視為一種 static object,其範圍 是「整個程式」
五.記憶體管理
先說一下new 和 delete 的調用過程
1. new:先分配 memory, 再調用 ctor
Complex* pc = new Complex(1,2);
編譯器會把它翻譯成
void* mem = operator new( sizeof(Complex) ); //分配內存,其內部調用mallocpc = static_cast<Complex*>(mem); //轉型pc->Complex::Complex(1,2); //構造函數 pc->Complex::Complex(1,2);
2. delete:先調用 dtor, 再釋放 mxemory
Complex* pc = new Complex(1,2);...delete pc;
編譯器轉化為
Complex::~Complex(pc); // 解構函式operator delete(pc); // 釋放內存 其內部調用feee()
下面是說一下動態分配所得的記憶體塊以VC編譯器為例
debug版 complex類內涵兩個double型成員變數(實部虛部)
每格4位元組
是不是感到有些驚訝,在debug版下 我們僅new complex() 到底給我們帶來多少的記憶體呢
頭部和尾部(紅色部分)的00000041是 cookie 是兩個4位元組記憶體 cookie負責標記記憶體 最後一位的1是代表獲得記憶體 如果最後一位是0回收記憶體 4*2 = 8bit
灰色部分是VC分期記憶體是賦予的每塊debug記憶體都會有這塊記憶體 4*8 + 4 = 36bit
綠色部分是我們的conplex 8bit
青色部分是補位部分,因為VC下每一塊記憶體必須是16的倍數
那麼我們new一個complex系統應該分配給 (4*2) + 36 + 8 + (4*3 補位) = 64bit
那麼非debug版
4*2 + 8 = 16;
下面我們來看一下帶指標的string類
debug版
4+(32+4)+(4*2) = 48
非debug版
4+(4*2) + 4 = 16
可見指標佔用的記憶體小一些
總結
1.帶有指標的class必須重寫Big Three這是一個非常良好的習慣
2.指標更省記憶體
如有不正確的地方請指正
參照<<侯捷 C++物件導向進階編程>>
C++物件導向進階編程(三)