7.2 訪問控制與封裝
7.2.1 訪問說明符
7.2.2 友元
7.3 類的其他特性
7.4 類的作用域
7.5 建構函式高階
7.6 類的靜態成員
類的基本思想
封裝在成員函式內部,任何對類成員的直接訪問都被看做this的隱式掉用,也就是當我們使用成員變數bookno的時候,實際上隱式的使用了this指向的成員,就像this->bookno
一樣
std::string isbn() const
常量物件以及常量物件的引用或指標都只能呼叫常量成員函式
建構函式的任務是初始化類物件的資料成員,無論何時只要類的物件被建立,就會執行建構函式
7.1.4.1 預設建構函式
如果類沒有定義任何建構函式,物件會執行預設初始化,類通過預設建構函式來控制預設初始化的過程
某些類不能依賴於合成的預設建構函式,合成的預設建構函式只適合非常簡單的類,對於普通的類,必須定義它自己的預設建構函式,原因有三
編譯器只有在發現類不包含任何建構函式的情況下才會替我們生成乙個預設的建構函式,一旦我們定義了其他的建構函式,除非我們再定義乙個預設建構函式,否則類將沒有預設建構函式
class a
};int main()
對於某些類,合成的預設建構函式可能執行錯誤的操作,例如陣列和指標被預設初始化,他們的值將是未定義的
有的時候編譯器不能為某些類合成預設的建構函式,例如類中包含乙個其他類型別的成員且這個成員的型別沒有預設建構函式
= default
形如a() = default
的建構函式
7.1.4.2 拷貝,賦值與析構
struct和class的區別僅僅是形式上有所不同,我們可以用兩個關鍵字中的任何乙個定義類,唯一的區別是預設訪問許可權不一樣
類可以允許其他類或者其他函式訪問它的非公有成員,方法是令其他類或者函式成為他的友元(friend)
如下,在screen的public部分定義了pos,這樣使用者就可以使用pos,screen的使用者不應該知道screen使用了乙個string物件存放它的資料,因此通過把pos定義為public可以隱藏screen的細節
class screen ;
有時我們希望能修改類的某個資料成員,即使是在乙個const成員函式內,可以通過在變數的宣告中加入mutable關鍵字來做到這一點。
class a ;
void a::some_member() const
有時我們希望某個成員開始時總是擁有乙個預設初始化的值,c++11中最好的方式是把這個預設值宣告成乙個類內初始值
class window_mgr ;
};
class screen
screen& set(char c)
};//main
screen myscreen;
myscreen.mov(4,0).set('#'); //把游標移動到指定位置然後設定該位置的字元值
如果從const成員函式返回this,那麼此時this應該是乙個const指標,this是乙個const物件,這種時候我們就不能把this指標嵌入到動作的序列中了
myscreen.displyay(cout).set('*'); //const成員函式display返回了常量引用,set會發生錯誤
通過區分成員函式是否是const的,我們可以對其進行過載
struct a
const int &fun() const
};
類之間的友元關係
class a ;
class a ;
class a
a()
void g();
void h();
};void a::g()
void f(); //宣告f
void a::h()
//tips:編譯器有時候並不強制執行上面的友元限定規則
名字查詢:尋找與所用名字最匹配類的宣告
對於類內部成員函式來說,名字查詢與上述規則有所區別,類的定義分兩步處理
編譯器處理完類中的全部宣告後才會處理成員函式的定義
typedef double money;
class a ;
class a
a(int t):a(t),b(t),c(a){} //正確:顯式的初始化引用和const成員
private:
int a;
const int b;
int &c;
};
class a
//看上去是先用val初始化j,然後用j初始化i
//然而實際上是先用j初始化i,然後用val初始化j,很顯然第乙個行為是未定義的
}
因此,最好令建構函式初始值的順序與成員宣告的順序保持一致,並且盡量避免用某些成員初始化其他成員
如果乙個建構函式為所有引數提供了預設實參,則它實際上也定義了預設建構函式
class a ;
乙個委託建構函式使用它所屬類的其他建構函式執行她自己的初始化過程,或者說把它自己的一些(或者全部)職責委託給了其他建構函式
class sales_data
//其餘建構函式全部委託給另乙個建構函式
sales_data():sales_data("", 0, 0) {}
sales_data(std::string s):sales_data(s, 0, 0) {}
sales_data(std::istream &is): sales_data()
};
//路徑"999" -> string -> 類a
fun("99999"); //錯誤
fun(string("999")); //true:顯式的轉換為string,然後隱式轉換為a
fun(a("999")); //true:隱式轉換為string,然後顯式地轉換為a
class a ;
func(string("123")); //error
a a = string("123"); //error: explicit不能用於拷貝形式的初始化過程
fun(static_cast(string("123"))); //true:static_cast可以使用explicit的建構函式
標準庫中的explicit(e.g.)
聚合類使得使用者可以直接訪問它的成員,並且具有特殊的初始化語法形式,當類滿足以下條件的時候,它是聚合的
class data ;
data vall = ; //我們可以用花括號括起來的成員初始值列表初始化聚合類的資料成員
//初始值的順序必須和宣告的順序一致
data val2 = ; //error:不能用"anna"初始化val,2初始化s
//初始值列表的元素個數如果少於類的成員的數量,靠後的成員被值初始化
聚合類存在三個明顯的缺點
字面值常量類要求資料成員都是字面值型別的聚合類,或者滿足以下要求
constexpr可以宣告為=default的形式,否則它必須滿足兩個要求
constexpr建構函式必須初始化所有資料成員(初始值或使用constexpr建構函式或是常量表示式)
class a;
//呼叫static成員函式
double r = account::sdfunc();
//我們仍然可以通過物件呼叫static成員
a a;
a* pa = &a;
r = a.sdfunc(); //並且不需要通過作用域運算子了
r = pa->sdfunc();
//定義static函式,static只出現在類內部,內外部就不能加上這個關鍵字了
void svfunc(double a)
//靜態成員不屬於任何乙個物件,所以他們並不是在物件建立的時候被定義的,因此我們必須在類外定義和初始化每個靜態成員
//靜態資料成員定義在任何函式之外,一旦被定義就存在於程式的整個生命週期
double account::sd = sdfunc2();
通常情況下,類的static成員不應該在類內初始化,但是我們可以為他提供const整數型別的類內初始值,不過要求靜態成員必須是字面值常量型別的constexpr
靜態成員可以用於某些場景,普通成員不能
class a ;
c primer 第七章 類
const更大的作用是可以修飾函式的引數,返回值,定義體。1.用const 修飾函式的引數 如果引數作輸出用,不論它是什麼資料型別,也不論它採用 指標傳遞 還是 引用傳遞 都不能加const修飾,否則該引數將失去輸出功能。const 只能修飾輸入引數 如果輸入引數採用 指標傳遞 那麼加const修飾...
C Primer 第七章 類
成員函式 宣告在類內,定義可內可外,定義在類內則是隱式內聯函式 在成員函式內部,可以直接使用呼叫該函式的物件的成員 類外部定義的成員的名字必須包含所屬類名 this本身是乙個常量指標,預設情況下指向非常量版本 在引數列表後加const可以修改this指向常量型別 編譯器首先編譯成員的宣告,然後才輪到...
C Primer 筆記 第七章 類
類的成員函式通過乙個名為 this 的隱式形參來訪問呼叫它的那個物件,在呼叫時,用請求該函式的物件位址初始化 this,this 是乙個常量指標。要將 this 宣告成指向常量的指標只需在函式的形參列表後加上 const。定義在類內部的函式是隱式的 inline 函式。類外部定義的成員名字必須包含其...