cin cout
- C++ 中常使用 cin 、 cout 進(jìn)行控制臺(tái)的輸入、輸出
- cin 用的右移運(yùn)算符 cout 用的是左移運(yùn)算符
- endl 是換行的意思
int main() {
int age;
cin >> age;
cout << "age is" << age << endl;
}
函數(shù)重載
- 規(guī)則
- 函數(shù)名相同
- 參數(shù)個(gè)數(shù)不同,參數(shù)類型不同,參數(shù)順序不同
- 注意
- 返回值類型與函數(shù)重載無關(guān)
- 調(diào)用函數(shù)時(shí),實(shí)參隱式類型轉(zhuǎn)換可能會(huì)產(chǎn)生二義性
- 本質(zhì)
- 采用了 name mangling 或者叫 name decoration 技術(shù)
- C++ 編譯器默認(rèn)會(huì)對(duì)符號(hào)名(變量名、函數(shù)名等)進(jìn)行改編、修飾,有些地方翻譯為“命名傾軋”
- 重載時(shí)會(huì)生成多個(gè)不同的函數(shù)名,不同編譯器( MSVC 、 g++g++)有不同的生成規(guī)則
- 通過 IDA 打開 【 VS_Release_ 禁止優(yōu)化 】 可以看到
extern “C”
- 被
extern "C”
修飾的代碼會(huì)按照 C 語言的方式去編譯
extern "C" {
void func() {
cout << "func()" << endl;
}
void func(int a) {
cout << "func(int a) " << a << endl;
}
}
-
如果函數(shù)同時(shí)有聲明和實(shí)現(xiàn), 要讓函數(shù)聲明被 " extern " 修飾 ,函數(shù)實(shí)現(xiàn)可以不修飾
extern "C" void func(); extern "C" void func(int a); extern "C" { void func() { } void func(int a) { } }
由于 C 、 C++ 編譯規(guī)則的不同,在 C 、 C++ 混合開發(fā)時(shí),可能會(huì)經(jīng)常出現(xiàn)以下操作, C++ 在調(diào)用 C 語言 API 時(shí),需要使用 extern " 修飾 C 語言的函數(shù)聲明
-
有時(shí)也會(huì)在編寫 C 語言代碼中直接使用 extern “C” ,這樣就可以直接被 C++ 調(diào)用
extern-c1.png
volidate 關(guān)鍵字
- 不論何時(shí)都從內(nèi)存中讀取數(shù)據(jù),不使用編譯器的優(yōu)化后的值
默認(rèn)參數(shù)
- C++ 允許函數(shù)設(shè)置默認(rèn)參數(shù),在調(diào)用時(shí)可以根據(jù)情況省略實(shí)參。規(guī)則如下:
- 默認(rèn)參數(shù)只能按照右到左的順序
- 如果函數(shù)同時(shí)有聲明、實(shí)現(xiàn),默認(rèn)參數(shù)只能放在函數(shù)聲明中
- 默認(rèn)參數(shù)的值可以是常量、全局符號(hào)(全局變量、函數(shù)名)
- 函數(shù)重載、默認(rèn)參數(shù)可能會(huì)產(chǎn)生沖突、二義性(建議優(yōu)先選擇使用默認(rèn)參數(shù))
內(nèi)聯(lián)函數(shù)(inline function)
- 使用 inline 修飾函數(shù)的聲明或者實(shí)現(xiàn),可以使其變成內(nèi)聯(lián)函數(shù)
- 建議聲明和實(shí)現(xiàn)都增加 inline 修飾
- 特點(diǎn)
- 編譯器會(huì)將函數(shù)調(diào)用直接展開為函數(shù)體代碼
- 可以減少函數(shù)調(diào)用的開銷
- 會(huì)增大代碼體積
- 注意
- 盡量不要內(nèi)聯(lián)超過 10 行代碼的函數(shù)
- 有些函數(shù)即使聲明為 inline ,也不一定會(huì)被編譯器內(nèi)聯(lián),比如遞歸函數(shù)
內(nèi)聯(lián)函數(shù)與宏
- 內(nèi)聯(lián)函數(shù)和宏,都可以減少函數(shù)調(diào)用的開銷
- 對(duì)比宏,內(nèi)聯(lián)函數(shù)多了語法檢測(cè)和函數(shù)特性
- 思考以下代碼的區(qū)別
#define sum (x) (x +
inline int sum( int x ) { return x + x ;
int a = 10; sum(a++);
引用(Reference)
- 在 C 語言中,使用指針( Pointer )可以間接獲取、修改某個(gè)變量的值
- 在 C++ 中,使用引用( Reference )可以起到跟指針類似的功能
int age = 20; int &rage = age;
- 注意點(diǎn)
- 引用相當(dāng)于是變量的別名(基本數(shù)據(jù)類型、枚舉、結(jié)構(gòu)體、類、指針、數(shù)組等,都可以有引用)
- 對(duì)引用做計(jì)算,就是對(duì)引用所指向的變量做計(jì)算
- 在定義的時(shí)候就必須初始化,一旦指向了某個(gè)變量,就不可以再改變,“從一而終”
- 可以利用引用初始化另一個(gè)引用,相當(dāng)于某個(gè)變量的多個(gè)別名
- 不存在 【 引用的引用、指向引用的指針、引用數(shù)組 】
- 引用存在的價(jià)值之一:比指針更安全、函數(shù)返回值可以被賦值
const
- const 是常量的意思,被其修飾的變量不可修改
- 如果修飾的是類、結(jié)構(gòu)體(的指針),其成員也不可以更改
- 以下 5 個(gè)指針分別是什么含義?
int age = 10; const int *p0 = &age; int const *p1 = &age; int * const p2 = &age; const int * const p3 = &age; int const * const p4 = &age;
- 上面的指針問題可以用以下結(jié)論來解決:
- const 修飾的是其右邊的內(nèi)容
int age = 10; int score = 20; /* *p0 = 20; // 失敗, 不能改變指向變量的值 p0 = &score; // 成功,可以改變指針的指向 */ const int *p0 = &age; /* *p1 = 20; // 失敗, 不能改變指向變量的值 p1 = &score; // 成功,可以改變指針的指向 */ int const *p1 = &age; /* *p2 = 20; // 成功, 可以改變改變指向變量的值 p2 = &score; // 失敗,不能改變指針的指向 */ int * const p2 = &age; /** *p3 = 20; // 失敗, 不能改變改變指向變量的值 p3 = &score; // 失敗,不能改變指針的指向 */ const int * const p3 = &age; /** *p4 = 20; // 失敗, 不能改變改變指向變量的值 p4 = &score; // 失敗,不能改變指針的指向 */ int const * const p4 = &age;
常引用(Const Reference)
-
引用可以被 const 修飾,這樣就無法通過引用修改數(shù)據(jù)了,可以稱為常引用
- const 必須寫在 符號(hào)的左邊,才能算是常引用
-
const 引用的特點(diǎn)
- 可以指向臨時(shí)數(shù)據(jù)(常量、表達(dá)式、函數(shù)返回值等)
- 可以指向不同類型的數(shù)據(jù)
- 作為函數(shù)參數(shù)時(shí)( 此規(guī)則也適用于 const 指針
- 可以接受 const 和非 const 實(shí)參(非 const 引用,只能接受非 const 實(shí)參)
- 可以跟非 const 引用構(gòu)成重載
當(dāng)常引用指向了不同類型的數(shù)據(jù)時(shí),會(huì)產(chǎn)生臨時(shí)變量,即引用指向的并不是初始化時(shí)的那個(gè)變量
數(shù)組的引用
- 常見的 2 種寫法
int array[] = {10, 20, 30}; int (&ref)[3] = array; int * const &ref2 = array;
引用的本質(zhì)
- 就是指針,只是編譯器削弱了它的功能,所以引用就是弱化了的指針
- 一個(gè)引用占用一個(gè)指針的大小
類
-
C++ 中可以使用 struct 、 class 來定義一個(gè)類
- struct 和 class 的區(qū)別
- struct 的默認(rèn)成員權(quán)限是 public
- class 的默認(rèn)成員權(quán)限是 private
// 類的定義 struct Person { // 成員變量 int m_age; // 成員函數(shù) void run() { cout << m_age <<"run()" <<endl; } } class Person { public: int m_age; void run() { cout << m_age <<"run()" <<endl; } } // 訪問person對(duì)象 Person person; person.m_age = 20; person.run(); // 通過指針訪問person對(duì)象 Person *p = &person; p->m_age = 30; p->run();
- struct 和 class 的區(qū)別
上面代碼中 person 對(duì)象、 pPerson 指針的內(nèi)存都是在函數(shù)的棧空間,自動(dòng)分配和回收的, person對(duì)象地地址就是成員變量m_age的地址
可以嘗試反匯編 struct 和 class ,看看是否有其他區(qū)別
實(shí)際開發(fā)中,用 class 表示類比較多
對(duì)象的內(nèi)存布局
- 成員變量的類型相同時(shí),對(duì)象的內(nèi)存為所有成員變量的內(nèi)存之和
- 類型不同時(shí),采用內(nèi)存對(duì)齊方式計(jì)算
- 空對(duì)象占用的內(nèi)存空間為:1:c++編譯器會(huì)給每個(gè)空對(duì)象分配一個(gè)字節(jié)空間,是為了區(qū)分空對(duì)象占用內(nèi)存的位置
- 每個(gè)空對(duì)象也應(yīng)該有一個(gè)獨(dú)一無二的內(nèi)存地址
// 類的定義
struct Person {
// 成員變量
int m_id;
int m_age;
int m_height;
// 成員函數(shù)
void display() {
cout << m_id << endl;
cout << m_age << endl;
cout << m_height << endl;
}
}
Person person;
person.m_id = 10;
person.m_age = 20;
person.m_height = 30;
// 創(chuàng)建指針
Person *pPerson = (Person*)&person.m_age; // 指針指向person對(duì)象對(duì)象的第一個(gè)地址
pPerson->m_id = 40; // 相當(dāng)于是person對(duì)象的第一個(gè)地址賦值,給m_age賦值
pPerson->m_age = 50;
person.display(); //輸出 10 40 50
this
- this 是指向當(dāng)前對(duì)象的指針
- 對(duì)象在調(diào)用成員函數(shù)的時(shí)候,會(huì)自動(dòng)傳入當(dāng)前對(duì)象的內(nèi)存地址
- 指向被調(diào)用的成員函數(shù)/變量所屬的對(duì)象
- 當(dāng)形參和成員變量同名時(shí)i,可用this指針區(qū)分
- 在類的非靜態(tài)成員函數(shù)中返回對(duì)象本身,可使用 return *this
- this指針的本質(zhì)是指針常量,指針的指向不可以修改的
struct Person {
// 成員變量
int m_id;
int m_age;
int m_height;
// 成員函數(shù)
void display() {
cout << "m_id is " << this->m_id << endl;
cout << "m_age is " << this->m_age << endl;
cout << "m_height is " << this->m_height << endl;
}
};
class Perosn {
public:
static int m_age;
Perosn& addAge(int age) {
this->m_age = age;
return *this;
}
}
void testPerson() {
Perosn p;
p.addAge(10).addAge(10).addAge(10);
}
封裝
- 成員變量私有化,提供公共的 getter 和 setter 給外界去訪問成員變量
struct Person {
private:
// 成員變量
int m_id;
public:
int m_age;
int m_height;
// 成員函數(shù)
void display() {
cout << "m_id is " << this->m_id << endl;
cout << "m_age is " << this->m_age << endl;
cout << "m_height is " << this->m_height << endl;
}
void setId(int id) {
this->m_id = id;
}
int getId() {
return this->m_id;
}
};
內(nèi)存空間的布局
- 每個(gè)應(yīng)用都有自己獨(dú)立的內(nèi)存空間,其內(nèi)存空間一般都有以下幾大區(qū)域
- 代碼段(代碼區(qū))
- 用于存放代碼
- 數(shù)據(jù)段(全局區(qū))
- 用于存放全局變量等
- 靜態(tài)變量
- 存放常量(符串常量,const修飾的全局變量)
- 代碼段(代碼區(qū))
- ??臻g
- 存放局部變量,函數(shù)參數(shù)
- 棧區(qū)的數(shù)據(jù)有編譯器管理開辟和釋放
- 注意:不要返回局部變量的地址
- 每調(diào)用一個(gè)函數(shù)就會(huì)給它分配一段連續(xù)的??臻g,等函數(shù)調(diào)用完畢后會(huì)自動(dòng)回收這段??臻g
- 自動(dòng)分配和回收
- 堆空間
- 需要主動(dòng)去申請(qǐng)和釋放
- 由程序員分配釋放,當(dāng)程序結(jié)束時(shí),由操作系統(tǒng)回收
- 在C++中主要利用new在堆去開辟內(nèi)存, 利用delete釋放
堆空間
- 在程序運(yùn)行過程,為了能夠自由控制內(nèi)存的生命周期、大小,會(huì)經(jīng)常使用堆空間的內(nèi)存
- 堆空間的申請(qǐng) 釋放
- malloc \ free
- new \ delete
- new [] delete[]
-
注意
- 申請(qǐng)堆空間成功后,會(huì)返回那一段內(nèi)存空間的地址
- 申請(qǐng)和釋放必須是 1 對(duì) 1 的關(guān)系,不然可能會(huì)存在內(nèi)存泄露
-
現(xiàn)在的很多高級(jí)編程語言不需要開發(fā)人員去管理內(nèi)存(比如 Java ),屏蔽了很多內(nèi)存細(xì)節(jié),利弊同時(shí)存在
- 利:提高開發(fā)效率,避免內(nèi)存使用不當(dāng)或泄露
- 弊:不利于開發(fā)人員了解本質(zhì),永遠(yuǎn)停留在 API 調(diào)用和表層語法糖,對(duì)性能優(yōu)化無從下手
堆空間初始化
int *p0 = (int*)malloc(sizeof(int)); //p1 未初始化
memset(p0, 0, sizeof(int*)); // 將p1的每一個(gè)字節(jié)都初始化為0
int *p1 = new int; //未初始化
int *p2 = new int(); //被初始化為0
int *p3 = new int(3); // 被初始化為5
int *p4 = new int[3]; // 數(shù)組元素未被初始化
int *p5 = new int[3](); // 3個(gè)數(shù)組元素被初始化為0
int *p6 = new int[3]{}; // 3個(gè)數(shù)組元素被初始化為0
int &p7 = new int[3]{5}; // 數(shù)組首元素被初始化為6,其他元素初始化為0
對(duì)象的內(nèi)存
-
對(duì)象的內(nèi)存可以存在于 3 種地方
- 全局區(qū)(數(shù)據(jù)段):全局變量
- ??臻g:函數(shù)里面的局部變量
- 堆空間:動(dòng)態(tài)申請(qǐng)內(nèi)存( malloc 、 new 等)
// 全局區(qū) Person person; int test() { // ??臻g Person person; // 堆空間 Person * p = new Person; return 0; }
構(gòu)造函數(shù)(Constructor)
- 構(gòu)造函數(shù)(也叫構(gòu)造器),在對(duì)象創(chuàng)建的時(shí)候自動(dòng)調(diào)用,一般用于完成對(duì)象的初始化工作
- 特點(diǎn)
- 函數(shù)名與類同名,無返回值( void 都不能寫),可以有參數(shù),可以重載,可以有多個(gè)構(gòu)造函數(shù)
- 一旦自定義了構(gòu)造函數(shù),必須用其中一個(gè)自定義的構(gòu)造函數(shù)來初始化對(duì)象
- 注意
- 通過 malloc 分配的對(duì)象不會(huì)調(diào)用構(gòu)造函數(shù)
- 一個(gè)廣為流傳的、很多教程 書籍都推崇的錯(cuò)誤結(jié)論
- 默認(rèn)情況下,編譯器會(huì)為每一個(gè)類生成空的無參的構(gòu)造函數(shù)
- 正確理解:在某些特定的情況下,編譯器才會(huì)為類生成空的無參的構(gòu)造函數(shù)
默認(rèn)情況下,成員變量的初始化
struct Person {
int m_age;
Person() {
/// 將所有成員變量清0
memset(this, 0, sizeof(Person));
cout << "Person()" << endl;
}
Person(int age) {
m_age = age;
cout << "Person(age)" << endl;
}
};
// 全局區(qū):成員變量初始化為0
Person g_person0; // Person()
Person g_person1(); // 不會(huì)調(diào)動(dòng)構(gòu)造方法,僅僅是函數(shù)的聲明
Person g_person2(10); // Person(int)
void test() {
// ??臻g:沒有初始化成員變量
Person person0;// Person()
Person person1(); // 不會(huì)調(diào)動(dòng)構(gòu)造方法,僅僅是函數(shù)的聲明
Person person2(30); // Person(int)
// 堆空間:沒有初始化成員變量
Person * p0 = new Person;// Person()
Person * p1 = new Person();// Person()
Person * p2 = new Person(30); // Person(int)
Person person1; //??臻g(成員變量不會(huì)不被初始化)
// 堆空間
Person * p2 = new Person; // 成員變量不會(huì)被初始化
Person * p3 = new Person(); // 成員變量初始化為0
Person * p4 = new Person[3]; /// 成員變量不會(huì)被初始化
Person * p5 = new Person[3]();//3個(gè)person對(duì)象的成員變量都初始化為0
Person * p6 = new Person[3]{}; // 3個(gè)person對(duì)象的成員變量都初始化為0
}
- 如果自定義了構(gòu)造函數(shù),除了全局區(qū),其他內(nèi)存空間的成員變量默認(rèn)都不會(huì)被初始化,需要開發(fā)人員手動(dòng)初始化
構(gòu)造函數(shù)的分類及調(diào)用
- 按參數(shù)分類:有參構(gòu)造和無參構(gòu)造
- 按類型分為: 普通構(gòu)造和拷貝構(gòu)造
三種調(diào)用方式
- 括號(hào)法
Person p = p(10);
- 顯示法
Person p = Person(10);
- 隱式轉(zhuǎn)換法
Person p = 10; // 相當(dāng)于寫了 Person p = Person(10)
構(gòu)造函數(shù)調(diào)用規(guī)則
- 默認(rèn)情況下(有需要處理事的時(shí)候),c++編譯器至少給一個(gè)類添加3個(gè)函數(shù)
- 默認(rèn)構(gòu)造函數(shù)
- 默認(rèn)析構(gòu)函數(shù)
- 默認(rèn)拷貝函數(shù)
- 如果用戶定義有參構(gòu)造函數(shù),c++不再提供默認(rèn)無參構(gòu)造函數(shù),但是會(huì)提供默認(rèn)的拷貝構(gòu)造
- 如果用戶定義拷貝構(gòu)造函數(shù), c++不會(huì)再提供其他構(gòu)造函數(shù)
- 當(dāng)其他類對(duì)象作為本類成員,構(gòu)造時(shí)候先構(gòu)造類對(duì)象,再構(gòu)造自身,析構(gòu)的順序與構(gòu)造相反
析構(gòu)函數(shù)(Destructor)
- 析構(gòu)函數(shù)(也叫析構(gòu)器),在對(duì)象銷毀的時(shí)候自動(dòng)調(diào)用,一般用于完成對(duì)象的清理工作
- 特點(diǎn)
- 函數(shù)名以 開頭,與類同名,無返回值( void 都不能寫),無參,不可以重載,有且只有一個(gè)析構(gòu)函數(shù)
- 注意
- 通過 malloc 分配的對(duì)象 free 的時(shí)候不會(huì)調(diào)用構(gòu)造函數(shù)
- 構(gòu)造函數(shù)、析構(gòu)函數(shù)要聲明為 public ,才能被外界正常使用