從C過度到C++,你必須要掌握的知識點
內容原創,未經本人同意請勿轉載。聯絡本人:[email protected]
1,virtual函式
函式之前加上virtual關鍵字就表示該函式為虛擬函式,在派生類中通過重寫該虛擬函式來實現對基類函式的覆蓋。
//基類中定義virtual函式 class base { public: virtual void fun() {cout<<"BASE";} }; //派生類中覆蓋fun函式 class derived: base { public: //不管該函式之前是否新增virtual欄位,該函式都是虛擬函式 void fun() {cout<<"DERIVED";} }; /*多型使用*/ int main() { base* d = new derived(); /*類的多型,呼叫的是派生類中的fun函式*/ d->fun(); } //輸出 DERIVED // void call_fun(base* b) { //如果b是base類的例項,就呼叫base中的fun //如果b是derived類的例項,就呼叫derived中的fun b->fun(); }
為何”虛”—動態聯編
virtual函式用到了動態聯編和推遲聯編的技術,virtual函式在編譯的時候是無法確定的,而是在執行的時候被確定的。
編譯器在發現類中有virtual函式的時候,就會為該類分配一個VTABLE函式指標陣列,這個數組裡存放了類中的所有虛擬函式。
一個類只有一個VTABLE,不管有多少個例項
派生類有各自的VTABLE
同一個虛擬函式在基類和派生類的VTABLE的相同位置
編譯器在編譯的時候為每個例項在記憶體中分配一個vptr欄位,該欄位指向本例項的VTABLE
void call_fun(base* b)
{
//如果b是base類的例項,就呼叫base中的fun
//如果b是derived類的例項,就呼叫derived中的fun
//編譯後該函式b->fun();變成
(base->vptr[1])();
//這樣根據傳遞進來的例項不同,就呼叫不同的函式。
}
純虛擬函式(interface類)
class base
{
public:
virtual fun() = 0; //0標誌一個虛擬函式為純虛擬函式
}
純虛擬函式表示該類是一個抽象類,無法被例項化,只能被派生類繼承覆蓋。用來規範派生類,這就是所謂的“介面”類,用來約束派生類需要實現這些函式。
2,命令空間
定義
namespace ns1
{
int a;
int b;
class base{};
void fun(){}
}
分離式定義
one.h
#ifndef TWO_H_
#define TWO_H_
namespace two
{
void say();
}
#endif
two.h
#ifndef TWO_H_
#define TWO_H_
namespace two
{
void say();
};
#endif
one_two.cpp
#include <iostream>
#include "one.h"
#include "two.h"
void one::say()
{
cout<<"one say\r\n";
}
void two::say()
{
cout<<"two say\r\n";
}
//如果宣告的空間有類如何實現????
使用
若想使用某個識別符號,using 空間名::識別符號;
若想使用改namespace下的所有識別符號 using namespace 空間名;
//方法1
using namespace one;
//方法2
using one::say;
自定義空間名使用
#include <iostream>
#include <string>
using namespace std;
using namespace one;
using namespace two;
//全域性函式
void say()
{
cout<<"global say\r\n";
}
int main()
{
//全域性函式,一定要加上::
//否則會出現 錯誤:呼叫過載的‘say()’有歧義
::say();
one::say();
two::say();
}
3,模板
模板可以實現邏輯相同,但是資料型別不同的程式碼複製。當用戶使用模板時,引數由編譯器決定,這很像巨集
。
模板分為函式模板
和類模板
。
函式模板
定義&使用
template <型別引數表> 返回型別 函式名(形參列表) {函式實體}
template <typename T> void print(const T& var)
{
cout<<var<<endl;
}
int main()
{
String a("hello template");
int num = 123;
print(a);
print(num);
}
/**********多個引數***********/
template <class T> T min(T ii, T jj, T kk)
{
T temp;
if(ii < jj) temp = ii;
else temp = jj;
if(temp > kk) temp = kk;
return temp;
}
int main()
{
int minNum = min(100, 30, 102);
cout<<minNum<<endl;
char minChar = min('z', 'a', 'h');
cout<<minChar<<endl;
return 0;
}
類模板
4,操作符過載
4.1,怎麼定義該函式
使用operator xx(xx表示操作符,例如==,+,-,等)
class person{
private:
int age;
public:
person(int a):age(a){};
inline operator == (const person& p) const;
};
inline person::operator==(const person& p) const
{
if(this->age == p.age)
{
return true;
}
else
{
return false;
}
}
using namespace std;
void main()
{
person p1(10);
person p2(20);
if(p1 == p2)
{
cout<<"the age equal"<<endl;
}
else
{
cout<<"the age different"<<endl;
}
}
4.2,為什麼要過載
對於系統的所有操作符,一般情況下,只支援基本資料型別和標準庫中提供的class,對於使用者自己定義的class,如果想支援基本操作,比如比較大小,判斷是否相等,等等,則需要使用者自己來定義關於這個操作符的具體實現。比如,判斷兩個人是否一樣大,我們預設的規則是按照其年齡來比較,所以,在設計person 這個class的時候,我們需要考慮操作符==,而且,根據剛才的分析,比較的依據應該是age。那麼為什麼叫過載呢?這是因為,在編譯器實現的時候,已經為我們提供了這個操作符的基本資料型別實現版本,但是現在他的運算元變成了使用者定義的資料型別class,所以,需要使用者自己來提供該引數版本的實現。
4.3,操作符過載為全域性函式
估計沒人會這麼使用
對於全域性的操作符過載函式,左運算元的引數必須被顯式的定義。
bool operator == (person const &p1, person const &p2);
如何決定使用全域性還是類操作符過載函式呢?
- 1 左操作符和比較物件是不是同一個型別
- 2 C++要求,=、[]、()、->、必須定義為類操作符過載函式
5,過載函式
5.1 什麼叫過載函式?
在同一個作用域內,可以有一組具有名字相同,引數不同的一組函式。過載函式用來命名一組功能相似的函式。
5.2 為什麼要用過載函式
- 必須寫很多函式名,來完成功能相似的一組函式
- 類建構函式,如果沒有過載。那如果要例項化不同的類是比較麻煩的。
- 操作符過載本身也是函式過載,豐富了已有操作符的功能。
5.3 編譯器如何解決命名衝突
編譯器會把不同引數名字相同的函式,用新的函式名取代。恩,其實也就還是不同名字,但是寫起來方便很多
5.4 重寫函式(override)
子類重新定義父類中有相同名稱、相同引數的虛擬函式。
- 被重新定義的函式不能為static函式
- 重寫的函式一定要完全相同(包括返回值、引數)
- 重寫的函式可以有不同的修飾符,例如在基類中是private,派生類可以寫成public、protected
5.5 重定義函式(redefining)
子類重新定義父類中具有相同名字的函式(引數列表可以不同)。
6,static類
6.1 static成員函式
static資料成員是儲存在程式的靜態儲存區,而並不是在棧空間上。獨立於任何類的物件。
注意:static成員函式
* 沒有this指標。因為static成員函式不是任何物件的組成部分
* 不能宣告為const型別,不能訪問非static成員,也不能訪問static const成員。
6.2 static成員
注意:在類中不能對static成員進行初始化
class person{
private:
static int age;
//static int age= 20; //錯誤:ISO C++ 不允許在類內初始化非常量靜態成員'person::age'
static string name;
public:
void print()
{
cout<<"name: "<<name<<" age: "<<age<<endl;
}
}
int person::age = 30;
string person::name = "kevin";
int main()
{
//int person::age = 20; 錯誤:對限定名‘person::age’的使用無效
person p;
p.print();
return 0;
}
內容原創,未經本人同意請勿轉載。聯絡本人:[email protected]
7,…
8,this
類中的成員函式,都有一個附件的隱含實參,該實參(this)就是一個指向該類物件的指標。
9,#include xx.h檔案和xx有何區別
10,訪問許可權
三種訪問許可權
- public 可以被任意實體訪問
- protected 只允許子類和本類成員函式訪問
- private 只允許本類成員函式訪問
三種繼承方式
- public繼承 不改變基類成員的訪問許可權
- protected繼承 使得基類中public變成protected,其他許可權不變。
- private繼承 使得基類中所有成員許可權變成private
class base{};
class deliverd : public base{}; //public繼承
11, 過載和覆蓋
12, new delete
13, const成員函式
若將成員成員函式宣告為const,則該函式不允許修改類的資料成員
1)const成員函式可以訪問非const物件的非const資料成員、const資料成員,也可以訪問const物件內的所有資料成員;
2)非const成員函式可以訪問非const物件的非const資料成員、const資料成員,但不可以訪問const物件的任意資料成員;
3)作為一種良好的程式設計風格,在宣告一個成員函式時,若該成員函式並不對資料成員進行修改操作,應儘可能將該成員函式宣告為const 成員函式。