1. 類概述
- 類的基本思想:數據抽象和封裝
2. 內聯函數
隱式內聯:
- constexpr函數爲隱式內聯函數;
- 在類內定義的函數爲隱式內聯函數;
顯式內聯
- 類內顯式inline聲明並定義;
- 類內顯式inline聲明,類外定義;
- 類內無顯式聲明,類外追加inline定義;
3. const成員函數
class Sales_data{
// 數據成員
std::string bookNo;
// const成員函數
std::string isbn() const {return bookNo;}
};
- 非const對象既可調用非const成員函數,也可以調用const成員函數;
- 而const對象只能調用const成員函數;
- 因爲非常量對象可以隱式的轉換成常量對象,而常量對象不可以轉換成非常量對象。
- 深層次的原因,我的另一篇博客:深入理解爲什麼要有const成員函數
4. this
- 成員函數的隱式形參,類似於python類中的self;
- this默認是一個指向非常量對象的常量指針,const成員函數的this形參是一個指向常量對象的常量指針;
- 返回*this的成員函數返回的是調用該函數的對象的引用:
Sales_data& Sales_data::combine(const Sales_data &data2)
{
......
return *this
}
如上:誰調用的combine函數,返回的就是哪個對象的引用。
5. 構造函數
- 構造函數不能被聲明成const,當創建一個const類對象時,直到構造函數完成初始化過程,對象才取得“常量”屬性,因此構造函數中可以向對象寫值。
class Sales_data{
// 數據成員
std::string bookNo; // 如果是默認初始化,就初始化爲空string
unsigned units_sold=0; // 提供流類內初始值,因此使用合成默認構造函數就會按照類內初始值進行初始化
double revenue=0.0; // 同上
// const成員函數
std::string isbn() const {return bookNo;}
//構造函數
Sales_data()=default; // 默認構造函數,但不是自動合成的!!是手動定義的
Sales_data(const string &s):bookNo(s){} // 構造函數初始值列表
Sales_data(const string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(p*n){} // 構造函數初始值列表
默認構造函數
- 如果不編寫構造函數,編譯器將自動合成默認的構造函數,即按照默認初始化或者類內初始值(c++11)來初始化數據;
構造函數初始值列表
- 初始化
Sales_data(const string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(p*n){}
- 結果等同於賦值:
Sales_data(const string &s, unsigned n, double p)
{
bookNo = s;
units_sold = n;
revenue = p*n;
}
類似於python的:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
-
特殊情況:如果數據成員是const或者引用,則必須使用構造函數初始值列表進行初始化,不能賦值。
例如:如果一個類的數據成員如下class ConstRef{ public: ConstRef(int); private: int i; const int ci; int &ri; }
則其構造函數必須爲:
ConstRef::ConstRef(int val):i(val),ci(val),ri(i){}
-
成員初始化的順序:
- 構造函數初始值列表的順序不會影響實際的初始化順序,實際的初始化順序與在定義中出現的順序一致,如下例的實際順序是先i後j,因此在構造函數初始值列表中使用i(j),即用j來初始化i是不對的。
class X{
int i;
int j;
public:
X(int val):j(val), i(j){} //這裏是錯誤的
};
隱式的類類型轉換
-
通過一個實參調用的構造函數定義了一條從構造函數參數類型向類類型隱式轉換的規則;
Sales_data(const string &s):bookNo(s){}
-
該構造函數定義了一個從const string 類型向Sales_data類型轉換的規則,即在需要使用Sales_data的地方,可以使用string代替:
string null_book="9999999"; item.combine(null_book);
-
但是,注意,只允許一步隱式轉換:
item.combine("9999999");//錯誤,因爲先轉換爲string,又轉換爲Sales_data; item.combine(string("9999999"));//正確,顯式轉換到string,然後隱式轉換到Sales_data; item.combine(Sales_data("9999999"));//正確,隱式轉換到string,然後顯式轉換到Sales_data;
抑制 隱式類類型轉換
- 構造函數聲明爲explicit:注意!explicit只對含一個參數的構造函數有效。
explicit Sales_data(const string &s):bookNo(s){}
- 此時,不存在參數類型向類類型的隱式轉換,因此下面的代碼是錯誤的:
string null_book="9999999"; item.combine(null_book);
- explicit構造函數只能用於直接初始化
Sales_data item1(null_book);//直接初始化,正確
Sales_data item2=null_book;//拷貝初始化,錯誤
6. 訪問控制
struct和class的區別
- 相同點:在訪問說明符public之後是共有對象,在private之後是私有對象;
- 不同點(唯一不同點):在第一個訪問說明符之前的成員,struct是public,class是private;
public, private, protected區別
訪問運算符 | public | private | protected |
---|---|---|---|
類外用戶 | √ | × | × |
類內成員 | √ | √ | √ |
派生類 | √ | × | √ |
友元 | √ | √ | √ |
7. 友元
友元函數
- 類的友元函數是非成員函數,但是可以訪問類的private成員;
- 聲明的方式:在普通的函數聲明前加一個friend關鍵字:
friend Sales_data add(const Sales_data&, const Sales_data&);
友元類
- 類還可以將其他類定義爲友元,友元類可以訪問該類的所有成員:
class Screen{
friend class Window_mgr; //Window_mgr的成員可以訪問Screen的所有成員
}
- 每個類負責控制自己的友元類或友元函數;
友元成員函數
- 類還可以指定其他類的某個成員函數爲友元,聲明時必須明確指出該成員函數屬於哪個類;
class Screen{
friend void Window_mgr::clear(ScreenIndex);
}
友元重載函數
- 如果想聲明一組重載函數爲友元,必須分別聲明,因爲他們是不同的函數;
8. 類的作用域
- 定義在類外部的成員,不僅要在函數名前使用作用域運算符,而且如果返回類型也是類成員,也必須使用作用域運算符訪問:
Window_mgr::ScreenIndex Window_mgr::addScreen(const Screen &s){
...
}
- 類編譯的順序:首先編譯所有聲明,直到整個類可見之後,再編譯成員函數體。因此成員函數和數據類型的定義可以不用區分先後順序。
9. 類的靜態成員
- 目的:有時需要一些成員與類本身相關,而不是與各個對象相關;
- 通過在成員聲明前加上關鍵字static使其與類關聯:
class Account{
public:
static double rate(){return interestRate;} //類內定義靜態成員函數
static void rate(double); //類內聲明,類外定義靜態成員函數
private:
std::string owner; //普通成員
double amount; //普通成員
static double interestRate; //靜態成員
static double initRate(); //類內聲明,類外定義靜態成員函數
}
- 訪問類的靜態成員:既可以通過類的對象訪問,也可以通過作用域運算符直接訪問,成員函數可以直接使用靜態成員;
double r1,r2;
Account ac1;
r1=ac1.rate(); //通過對象訪問
r2=Account::rate(); //直接訪問
- 類外定義靜態成員:必須指定類名,且不需要static關鍵字(成員函數可以直接使用);
- 類的靜態成員必須在類外定義和初始化;
void Account::rate(double newRate)
{
interestRate = newRate; //不需要static關鍵字
}
- 靜態成員函數中不能訪問非靜態成員,因爲靜態成員屬於類,在類實例化之前就已經分配空間,而非靜態成員只有在實例化的時候纔會初始化;但非靜態成員函數可以訪問靜態成員;
- 類的靜態成員變量必須初始化;