1、成員函數(shù)末尾帶const : 不會修改該對象里任何成員變量的值
void noone() const {
Hour += 10; //錯誤,常量成員函數(shù)不可以修改稿成員變量的值
}
2、mutable 修飾符來修飾一個成員變量,這個成員變量處于可變狀態(tài),即使是以const 結(jié)尾的成員函數(shù)中也能修改。
mutable int Hour;
void noone() const {
Hour += 10; //可以修改成員變量Hour了。
}
3、static 修飾的成員變量(靜態(tài)成員變量),在類中聲明但沒有定義,未分配內(nèi)存,需要正在cpp源文件頭定義來保證任何調(diào)用函數(shù)之前這個靜態(tài)變量已經(jīng)被初始化
// .h 文件中
public:
static int mystatic; //聲明靜態(tài)成員變量但沒有定義
static void mystaticfunc(int testvalues); // 聲明靜態(tài)成員函數(shù)
// .cpp文件中
int Time::mystatic = 5; // 可以不給初始值,默認(rèn)為0,定義時不要加static
4、= default : 編譯器自動生成函數(shù)體,等價于 {}, 只適用于特殊的構(gòu)造函數(shù)。 = delete :顯式的禁用某個函數(shù)
5、拷貝構(gòu)造函數(shù):如果一個類的構(gòu)造函數(shù)的第一個參數(shù)是所屬的類類型引用,若有額外的參數(shù),那么這些額外的參數(shù)都有默認(rèn)值,如果定義了自己的拷貝構(gòu)造函數(shù),那么就取代了編譯器合成的拷貝構(gòu)造函數(shù),這個時候就必須在自己的拷貝構(gòu)造函數(shù)中給類中成員逐個賦值,第一個參數(shù)帶const,不帶explicit修飾。
Time myTime; //這會調(diào)用默認(rèn)構(gòu)造函數(shù)(不帶參的)
Time myTime2 = myTime; //調(diào)用了拷貝構(gòu)造函數(shù)
Time myTime3 ( myTime); //調(diào)用了拷貝構(gòu)造函數(shù)
Time myTime4 { myrine }; //調(diào)用了拷貝構(gòu)造函數(shù)
Time myTime5 = ( myTime}; //調(diào)用了拷貝構(gòu)造函數(shù)
Time myTime6; //這會調(diào)用默認(rèn)構(gòu)造函數(shù)(不帶參的)
myTime6 = myTime5; //沒有調(diào)用拷貝構(gòu)造函數(shù)
// 拷貝構(gòu)造函數(shù)
Time::Time(const Time& timem, int a)
6、函數(shù)遮蔽:子類中如果有一個同名函數(shù),那么父類中不管有幾個同名函數(shù),子類中都無法訪問,如果我們確實想調(diào)用父類中的同名函數(shù),有如下方法:
// (1)在子類的成員函數(shù)中用“父類::函數(shù)名”強制調(diào)用父類函數(shù)
// (2) 使用using,通過using讓父類的同名函數(shù)在子類中可見
public:
using Human::sameNameFunc: // 只能指定函數(shù)名
7、基類中某個函數(shù)聲明為虛函數(shù),則所有子類中它都是虛函數(shù)。父類指針指向子類對象,調(diào)用子類同名同參函數(shù),則需要把父類這個函數(shù)聲明為虛函數(shù)(virtual)
Human *human = new Men;
human->eat(); //如果父類eat聲明為virtual,則調(diào)用的是子類Men的eat函數(shù)
human->Human::eat(); // 如果函數(shù)前加了父類命名空間,則直接調(diào)用父類中的eat,即使父類聲明為virtual。
8、為了避免在子類中寫錯虛函數(shù),在C++11中,可以在子類函數(shù)聲明中加override(虛函數(shù)專用),子類覆蓋父類的同名同參函數(shù),編譯器進(jìn)行糾錯。
9、final 也是虛函數(shù)專用,用在父類中,如果我們在父類的函數(shù)聲明中加了final,那么任何嘗試子類覆蓋父類的該函數(shù)都會失敗
10、純虛函數(shù):在基類中聲明的純虛函數(shù),要求子類中去實現(xiàn)。一旦一個類中有純虛函數(shù),就不能生成這個類的對象了,這個類成為了“抽象類”
// 1)抽象類不能生成對象,統(tǒng)一管理子類對象
// 2)子類中必須要實現(xiàn)該基類中定義的純虛函數(shù)
virtual void eat() = 0; // 沒有函數(shù)體,只有聲明
11、當(dāng)定義一個子類對象時,先執(zhí)行的是父類的構(gòu)造函數(shù)體,再行子類構(gòu)造函數(shù)體,當(dāng)對象被系統(tǒng)回收時,先執(zhí)行子類的析構(gòu)函數(shù),再執(zhí)行父類的函數(shù)體。
12、用基類指針new子類對象,在delete的時候系統(tǒng)不會調(diào)用子類的析構(gòu)函數(shù),需要把父類的析構(gòu)函數(shù)聲明為虛函數(shù)才行 - 虛析構(gòu)函數(shù)
Human *human = new Men;
delete human; // 沒有執(zhí)行子類的析構(gòu)函數(shù)
// 修改父類析構(gòu)函數(shù):
virtual ~ Human();
// 1)如果一個類想要做父類,務(wù)必要把這個類的析構(gòu)寫成virtual
// 2)普通類可以不寫析構(gòu)函數(shù),但如果是基類,就必須寫一個析構(gòu)函數(shù),必須是虛析構(gòu)函數(shù)
// 3)虛函數(shù)會增加內(nèi)存開銷,類里面定義虛函數(shù),編譯器就會給這個類增加虛函數(shù)表,這個表中存放虛函數(shù)地址等信息
13、每個類都負(fù)責(zé)控制自己的友元類和友元函數(shù),友元類關(guān)系的判斷,最終還是要看類定義中有沒有對應(yīng)的friend 類聲明:
(1)友元關(guān)系是不能被子類繼承的。
(2)友元關(guān)系是單向的,比如類B是類A 的友元類,但這并不表示類 A是類B的友元類。
(3)友元關(guān)系也沒有傳遞性,例如類B是類 A的友元類,類C是類 B的友元類,這并不代表類 C是類 A的友元類。
Class A
{
friend class B; // 不需要public、protected、private 修飾
private:
int data;
};
14、RTTI(Run Time Type identification):運行時類型識別
(1)dynamic_cast: 能夠?qū)⒏割愔羔樆蛞冒踩霓D(zhuǎn)換為子類的指針或引用,幫助開發(fā)者做安全檢查
Human *phuman = new Men;
Men *pman = dynamic_cast<Men *>(phuman);
if (pman != nullptr) {
cout<<"轉(zhuǎn)換成功"<<endl;
} else {
cout<<"轉(zhuǎn)換失敗"<<endl;
}
// 對于引用,如果轉(zhuǎn)換失敗,則系統(tǒng)會拋出std::bad_cast異常,try...catch(){}
Human *phuman = new Men;
Human &q = *phuman;
try {
Men &pman = dynamic_cast<Men &>(q);
cout<<"轉(zhuǎn)換成功"<<endl;
} catch(std::bad_cast) {
cout<<"轉(zhuǎn)換失敗"<<endl;
}
(2)typeid:返回指針或引用所指對象的實際類型,例如typeid(指針或引用),typeid(表達(dá)式),返回一個常量對象的引用(標(biāo)準(zhǔn)庫類型(type_info))
Human *phuman = new Men;
const std::type_info& tp = typeid(*phuman);
cout<<tp.name()<<endl; // 父類有虛函數(shù)則結(jié)果為Class Men,否者為class Human
(3)要想RTTI兩個運算符正常工作,那么基類中必須要有一個虛函數(shù)
// c++中,如果類里含有虛函數(shù)。編譯器就會對該類產(chǎn)生一個虛函數(shù)表。
// 虛函數(shù)表里有很多項,每一項都是一個指針。每個指針指向的是這個類里的各個虛函數(shù)的入口地址。
// 虛函數(shù)表項里,第一個表項很特殊(有些編譯器虛函數(shù)表第一項之前的內(nèi)存位置存放指針,指向這個類所關(guān)聯(lián)的type_info對彖),它指向的不是虛函數(shù)的入口地址,它指向的實際上是這個類所關(guān)聯(lián)的type_info對彖
Human *phuman = new Men:
const std::type_info& tp = typeid(*phuman);
//phuman對象里有一個我們看不見的指針,這個指針指向的是這個對象所在的類Men里的虛函數(shù)表。
15、左值引用,一般不能綁定右值,只能綁定到左值上,const引用可以綁定到右值,const引用特殊。
char *p = nullptr ; // 指針有空指針說法,引用沒有空引用說法,必須初始化,一般綁定左值。
int a = 1;
int &b{ a } //b 綁定到a,當(dāng)然可以
int &c; //不可以,引用必須要初始化
int &c = 1; //不可以,c要綁定到左值上,不能綁定到右值上
const int &c = 1; //可以,const 引用可以綁定到右值上,所以const 引用可以說比較特殊
// 上面這段代碼最后一行等價于下面這兩行:
int tempvalue = 1;
const int &c = tempvalue //可以把 tempvalve 看成一個臨時變量
16、右值引用,綁定到右值的引用,用“&&”,希望用右值引用來綁定到即將銷毀的或是一些臨時的對象上。C++ 11 引入目的是提高程序運行效率,將拷貝對象變成移動對象。
int a = 10;
int &c = 1; //不可以,c要綁定到左值上,不能綁定到右值上
int &&c = 1; //可以
int &&c = a; //不可以,不能將右值引用綁定到左值上
const int &c = 1; //可以
17、++i : 左值表達(dá)式, i++ : 右值表達(dá)式
++i 是直接給 i 變量加 1,然后返回i本身,因為i是變量,所以可以被賦值,因此是左值表達(dá)式??纯慈缦路独?int i = 5;
(++i) = 20;
i++先產(chǎn)生一個臨時變量來保存i的值用于使用目的,再給 i加 1,接著返回臨時變量,之后系統(tǒng)再釋放這個臨時變量,臨時變量被釋放掉了,不能再被賦值,因此是右值表達(dá)式??纯慈缦路独?int i = 5;
(i++) = 20; //語法錯誤,提示:表達(dá)式必須是可修改的左值
18、std::move : 把一個左值 強制 轉(zhuǎn)換成右值
int i= 10;
int &&r = i; // 錯誤,不能將右值引用綁定到左值上
int &&r = std::move(i); // 正確
19、臨時對象產(chǎn)生:
CTempValue sum;
sum = 1000; // 產(chǎn)生了一個臨時對象
CTempValue sum = 1000; // 不會產(chǎn)生臨時對象,為sum對象預(yù)留了空間,用1000構(gòu)造sum對象,而且是直接構(gòu)造在sum對象預(yù)留空間里。
20、移動構(gòu)造函數(shù):聲明和實現(xiàn)習(xí)慣性加noexcept通知編譯器該移動構(gòu)造函數(shù)不拋出任何異常提高編譯效率
A(A&& tempa) noexcept : m_pb(tempa.m_pb) {
tempa.m_pb = nullptr;
std::cout<< "移動構(gòu)造函數(shù)執(zhí)行" << std::endl;
}
21、虛繼承離不開虛基類,虛基類的特點就是無論這個類在繼承體系中出現(xiàn)多少次,派生類中都只會包含唯一一個共享的該類子對象
class A : virtual public B {...}
22、顯式類型轉(zhuǎn)換運算符
explicit operator int() const {
return m_i;
}