C++ Primer 筆記——類
1.定義在類內部的函數是隱式的inline函數。
2.因為this的目的總是指向“這個”對象,所以this是一個常量指針,我們不允許改變this中保存的地址。
3.常量成員函數:允許把const關鍵字放在成員函數的參數列表之後,此時緊跟在參數列表後面的const表示this是一個指向常量的指針。因為this是指向常量的指針,所以常量成員函數不能改變調用它的對象的內容。
4.常量對象,以及常量對象的引用或指針都只能調用常量成員函數。
5.編譯器分兩步處理類:首先編譯成員的聲明,然後才輪到成員函數體。因此成員函數體可以隨意使用類中的其他成員而無須在意這些成員出現的次序。
6.構造函數不能被聲明成const,當我們創建類的一個const對象時,直到構造函數完成初始化過程,對象才能真正取得其“常量”屬性。因此,構造函數在const對象的構造過程中可以向其寫值。
7.當類沒有聲明任何構造函數時,編譯器才會自動的生成默認構造函數,默認構造函數按照如下規則初始化類的數據成員:
如果存在類內的初始值,用它來初始化成員。
否則,默認初始化該成員。
8.我們可以使用default來要求編譯器生產構造函數。
class test { public: test() = default; test(int i) {} };
9.使用class和struct定義類唯一的區別就是默認訪問權限,class默認是private,struct默認是public。
10.類允許其他類或者函數訪問它的非公有成員,方法是另其他類或者函數成為它的友元,友元聲明只能出現在類定義的內部,也不受訪問控制符限制。
class test { friend int GetTestCount(const test& t); public: test() = default; private: int m_count; }; int GetTestCount(consttest& t) { return t.m_count; }
11.盡管當類的定義發生改變時無需更改用戶代碼,但是使用了該類的源文件必須重新編譯。
12.一個可變數據成員永遠不會是const,即使它是const對象的成員。因此一個const成員函數可以改變一個可變成員的值。
class test { public: test() = default; void change_count() const { m_count++; } private: mutable int m_count; };
13.一個const成員函數如果以引用的形式返回*this,那麽它的返回類型將是常量引用。
class test { public: test() = default; const test& change_count() const { return *this; } };
14.每個類負責控制自己的友元類或者友元函數。
class test { friend class best; private: int m_count; }; class best { public: int get_test_count() { return m_t.m_count; } private: test m_t; };
class test; class best { public: int get_test_count(const test& t); }; class test { friend int best::get_test_count(const test& t); private: int m_count; }; // 註意這個定義必須在test定義之後,否則不知道test類裏的細節會報錯 int best::get_test_count(const test& t) { return t.m_count; }
15.和其它局部作用域不同,在類中不能重復定義外層作用域中已經定義過的類型名字,即使定義相同。
typedef int my_int; class test { private: typedef int my_int; // 錯誤,重復定義,但是有的編譯器可以編譯過 my_int m_count; };
16.成員函數中使用名字時的查找順序是:成員函數->類->類外作用域。
int count; class test { private: int count; int add_count(int count) { this->count += count; // this->count指的是類的成員,count指的是參數 ::count++; // ::count指的是外層作用域的count } };
17.如果沒有在構造函數的初始化列表中初始化成員,則在構造函數體之前執行默認初始化。
18.如果類的成員是const或者引用或者沒有默認構造函數的類類型的話,必須在定義時或者初始化列表裏完成初始化。
class test { public: test(){}; // 錯誤,m_const沒有初始化 private: const int m_count; };
19.構造函數的初始化列表中成員的出現順序並不代表初始化的順序,而是按照在類中的定義順序來初始化。所以如果一個成員靠另一個成員來初始化的時候要註意。
class test { public: test() :m_base(0) // m_base的值是0 ,m_count(m_base) // m_count的值是未定義的 {}; private: int m_count; int m_base; };
20.如果一個構造函數為所有的參數都定義了默認實參,則這個類實際上定義了默認構造函數。
21.委托構造函數指的是一個構造函數調用其它構造函數來執行自己初始化的過程。
class test { public: test(int i,int j) { } // 非委托構造函數 test(int i) :test(i, 0) {} // 委托構造函數,委托了test(int,int)構造函數 test(int i) :test() {} // 委托構造函數,委托了默認構造函數 };
22.當已有的構造函數中沒有完全初始化所有變量的時候會執行默認構造函數。
class test { public: test(int i) {} private: int m_count; }; struct A { test t; }; A a; // 錯誤,不能為A合成默認構造函數 struct B { B() {} // 錯誤,t沒有初始值 test t; };
class test { public: test(int i) {} }; test t(); // 正確,t是聲明的一個函數 test t1; // 錯誤,test沒有默認構造函數
23.如果構造函數之接受一個實參,則它實際上定義了轉換為此類類型的轉換構造函數,但是這種轉換只允許一步類類型轉換。
class test { public: test(std::string str) {} }; void get_test(test t){} get_test("123"); // 錯誤,"123"既不是test類型,也不是string類型 get_test(std::string("123")); // 正確,string類型被轉換為test類型
但是其實上述操作完成轉換後我們並不能使用它,如果不想這種轉換可以用explicit
class test { public: explicit test(std::string str) {} }; void get_test(test t){} get_test(std::string("123")); // 錯誤,不允許這種隱式轉換
24.聚合類必須滿足以下條件:
- 所有成員都是public
- 沒有定義任何構造函數
- 沒有類內初始值
- 沒有基類,也沒有virtual函數
struct test { std::string name; int count; }; test t = { "test",1 };
25.數據成員都是字面值類型的聚合類是字面值常量類,或者符合下列條件的也是字面值常量類:
- 數據類型都必須是常量類型
- 類必須至少含有一個constexpr構造函數
- 如果一個數據成員含有類內初始值,則內置類型成員的初始值必須是一條常量表達式;或者如果成員屬於某種類類型,則初始值必須使用成員自己的constexpr構造函數
- 類必須使用析構函數的默認定義,該成員負責銷毀類的對象
因為構造函數沒有返回語句,而constexpr函數的唯一可執行語句就是返回語句,可以推斷出constexpr構造函數其實是空語句。
constexpr構造函數必須初始化所有數據成員,初始值或者使用constexpr構造函數,或者是一條常量表達式。
26.類的靜態成員存在於任何對象之外,對象中部包含任何與靜態數據成員有關的數據。類似的,靜態成員函數也不與任何對象綁定在一起,也不包含this指針。作為結果,靜態成員函數不能聲明成const的。
27.一般來說我們不能在類內部初始化靜態成員,在類的外部定義靜態成員的時候,不能重復static關鍵字,而且一旦被定義,將一直存在於程序的整個生命周期中。
28.靜態成員和普通成員的區別:
class test { test(int i = m_count); // static成員可以做默認實參,普通成員不可以 private: static int m_count; static test m_t; // 正確,靜態成員可以是不完全類型 test* m_t1; // 正確,指針可以是不完全類型 test m_t2; // 錯誤,數據成員必須是完全類型 };
C++ Primer 筆記——類