1. 程式人生 > >C++高階程式設計 第九章: 精通類和物件

C++高階程式設計 第九章: 精通類和物件

本章節最主要講了在類的構造和析構方面, 記憶體是怎樣分配的, 還有幾個關鍵字 : static, const, virtual, inline, 各種過載等等知識點.

1. 究竟什麼時候需要自己寫建構函式, 賦值函式, 拷貝建構函式?

只要你寫的這個類, 在類裡面會動態分配記憶體(就是new一個型別出來), 你就要自己寫一份 建構函式, 賦值函式, 拷貝建構函式. 這是為啥呢? 下面來看例子

class a{
    a(int a_v):value(a_v){}
    ~a();
private:
    int value;
}//a.h
class A{
public:
    A
(int height, int width); ~A(); private: int gao; int kuan; a **m_cell; }//A.h //建立一個N*N的矩陣
A::A(int height, int width){
    m_cell = new a* [height];
    for(int i=0;i<height;i++){
        m_cell[i] = new a[width];
    }

}//A.cpp

我們可以看到, 書上用new的方法建立了一個二維陣列(矩陣), 那麼理所當然, 有new就會有delete, 所以我們在解構函式裡面, 也要對分配的記憶體進行釋放.

A::~A(){
    for(int i=0;i<width;i++){
        delete[] m_cell[i];
    }
    delete[] m_cell;
}

OK處理完建構函式和解構函式後, 我們再來研究 賦值函式 和 拷貝建構函式
還記得什麼時候呼叫 賦值和拷貝建構函式嘛?

當一個物件不存在時, 就呼叫拷貝建構函式
當一個函式存在時, 呼叫賦值建構函式

OK我們來看以下兩種情況:

A(3,4);
printA(A);

首先我們先理解一個東西!很重要!
淺複製: 如果沒有寫自己的拷貝建構函式和賦值建構函式, 就會呼叫預設的函式去執行, 當遇到一個基礎型別需要用到這兩個函式時, 編譯器通常會採取淺複製的方法去實現, 具體就是從源物件直接複製給目標物件, 直接複製!!!
當我們構造一個A的時候, 由m_cell指標 指向每個矩陣的元素, 假設我存在一個函式printA(), 接收一個物件並且列印其矩陣時, 這個printA()函式是建立一個和m_cell指向同一個地址的指標, 相當於建立一個副本, 所以我們對這個副本進行修改時, 也會修改m_cell指向的值 , 更糟糕的是!!! 當這個printA()函式結束的時候, 會呼叫A 的解構函式, 導致釋放了副本指向的記憶體, 導致了m_cell 指向了一個未知區域.

A a(3,4),b(5,6);
b=a;

這個有什麼問題呢? 當我們構造完a和b 的時候, 他們各自的m_cell 指標指向了不同的兩個記憶體, 當我們執行 b=a 的時候, 就會看到b.m_cell 指向了 a.m_cell所指向的地址.導致了b.m_cell指向了一個未知區域.

所以對於存在動態分配記憶體的類, 應該編寫自己的賦值函式和拷貝建構函式, 這樣才能安全的使用這個類.
具體做法就是. 先get到源引數的每一個值, 然後自己賦值,拷貝建構函式自己再new動態記憶體去 儲存get得到的值.

2.static

靜態成員:
我是這麼理解這個static: 就是這個static 修飾了某一個成員後, 這個成員就是對於類來說 而不是 對於類的物件來說. 怎麼理解呢?

class a{
publicstatic int abc();
private:
    static int b;
}//a.h


int a::b=0;
static int a::abc(){b++;}
//a.cpp

這個時候, 我們這麼理解b, b就是這個a類之間的一個共享資料. 就相當於a類的”全域性變數”, 這個做法就像宣告一個全域性變數一樣, 不過作用範圍是a類們之間.
static靜態成員變數不能在類的內部初始化。在類的內部只是宣告,定義必須在類定義體的外部,通常在類的實現檔案中初始化

靜態方法:
我是這麼理解這個static: 和前面靜態成員一樣, 當一個函式我們要其的作用於類而不是類的物件時, 就是這個靜態方法 服務於a類們之間, 我們就把這個abc方法設定成靜態 .

注意:
1. 靜態方法因為作用於整個類, 而不是類的物件, 所以不存在this指標, 因為沒有物件存在了嘛.
2. 靜態方法只能訪問類的protected和private的靜態成員!!! 就是不可以在靜態方法裡面呼叫非靜態成員!!!

3.const

class a{
public:
    ~a();
    a(int aa):b(aa){}
    void abc() const;
private:
    const int b;
    static const int bb;
}

const成員:
const的東西 就是表明了 建立了之後就不可以改動了!!!
一旦改動就會報錯, const成員變數也不能在類定義處初始化,只能通過建構函式初始化列表進行,並且必須有建構函式。
const資料成員 只在某個物件生存期內是常量,而對於整個類而言卻是可變的。因為類可以建立多個物件,不同的物件其const資料成員的值可以不同。所以不能在類的宣告中初始化const資料成員,因為類的物件沒被建立時,編譯器不知道const資料成員的值是什麼。
const資料成員的初始化只能在類的建構函式的初始化列表中進行。因為const值需要在定義的時候立刻初始化 不能有先後順序,(和引用一樣)要想建立在整個類中都恆定的常量,應該用類中的列舉常量來實現,或者statc cosnt.

const成員函式: 在函式後面加一個const關鍵字目的就是為了不讓這個函式去修改任何值, 當我們建立一個const 物件, 這個物件只可以呼叫const 方法(如abc),當我們建立一個非const物件, 可以呼叫const函式或者非const函式. 當然也有例外就是當我們真的很想在const函式裡面修改某一個成員的值, 我們就這個值的宣告前 新增 mutable 關鍵字.

static const 成員初始化.. 這個要研究一下才行. mark一下!!!

4.使用介面類和實現類
基本原則: 定義兩個類: 一個介面類, 一個實現類, 介面類提供了實現類所提供的公共方法, 但它只有一個數據成員, 即指向一個實現類物件的指標. 介面類方法實現只是呼叫實現類物件上的應用方法. 123