C++學習筆記
參考
- https://github.com/weidongshan/cpp_projects
- 《C++ Primer Plus》
- C++ Standards Support in GCC
- GCC
GCC中有l(wèi)ibstdc++庫的實現(xiàn) - LLVM
LLVM中有l(wèi)ibc++庫的實現(xiàn) - C++參考手冊
- cplusplus
- 面向?qū)ο缶幊痰?大特點
- 封裝
- 繼承
- 多態(tài)
- struct 聲明的類里的成員都是public
- class 聲明的類的成員都是private的,需要通過類的public的成員函數(shù)來訪問private的成員變量
- this是一個指針,指向當前對象
- 引用命名空間里的類或者函數(shù):
- 使用命名空間A里的類B,有3種方法:
- 在引用B的位置都用
A::B - 在開頭加上
using A::B;,后面可以直接使用B - 在開頭加入
using namespace A;
- 在引用B的位置都用
- 使用命名空間A里的類B,有3種方法:
- C++里的打印需要引用頭文件iostream,然后用cout來輸出,在使用cout時需要導入std命名空間
- 如:
cout<<"Hello World"<<endl;
- 如:
- 重載:函數(shù)名相同,參數(shù)不同(類型、數(shù)量、順序不同)
- 注意: 這里不包括函數(shù)的返回值的類型
- 引用和指針:引用相當于變量的別名
int a = 100; int &b = a; int *c = &a; b的類型是引用,b相當于a的別名,b和a表示同一塊內(nèi)存,跟指針不同。 c的類型是指針,c有自己的存儲空間,其中存放的是a的地址。 - 構(gòu)造函數(shù):跟類名相同的成員函數(shù),只看名字,不看參數(shù)
- 有了構(gòu)造函數(shù),可以實現(xiàn)在定義對象時直接初始化,類似結(jié)構(gòu)體變量在定義時初始化那樣,例如:
class Person { private: int age; char *name; public: Person() {} /* 如果用戶沒有傳name,那么使用默認值"none" */ person(int age, char *name = "none") { this.age = age; this.name = name; } }; void main() { Person per; // 注意:不可以寫成Person per(); Person per2(10); Person *per3 = new Person; Person *per4 = new Person(); Person *per5 = new Person[2]; // 數(shù)組,調(diào)用兩次構(gòu)造函數(shù) Person *per6 = new Person(10, "Xiaoming"); delete per3; delete per4; delete [] per5; delete per6; } - 系統(tǒng)會默認提供一個無參數(shù)的構(gòu)造函數(shù)
- 如果自己實現(xiàn)了一個有參數(shù)的構(gòu)造函數(shù),那么也必須自己實現(xiàn)一個無參數(shù)的構(gòu)造函數(shù)
- 有了構(gòu)造函數(shù),可以實現(xiàn)在定義對象時直接初始化,類似結(jié)構(gòu)體變量在定義時初始化那樣,例如:
- 析構(gòu)函數(shù):跟類名相同的成員函數(shù),并且前面有一個
~
class Person {
public:
Person() {
cout << "Create\n" << endl;
}
~Person() {
cout << "Delete\n" << endl;
}
};
- 系統(tǒng)會默認提供一個無參數(shù)的析構(gòu)函數(shù)
int main(int argc, const char *argv[])
{
int i = 0;
// 通過new創(chuàng)建的對象存活到調(diào)用delete,或者進程結(jié)束
for (i = 0; i < 10; i++) {
Person *per = new Person();
}
// 通過下面的方式創(chuàng)建的對象類似函數(shù)的局部變量,在棧里分配
// 每個for循環(huán)調(diào)用構(gòu)造和析構(gòu)
for (i = 0; i < 10; i++) {
Person per;
}
return 0;
}
-
拷貝構(gòu)造函數(shù)
- 默認拷貝構(gòu)造函數(shù):值拷貝,如果有指針類型的成員,那么只拷貝指針的值
Person per; Person per2(per);- 自定義拷貝構(gòu)造函數(shù)
class Person { public: Person(Person &per) { this.name = new char[strlen(per.name)+1]; strcpy(this.name, per.name) } } -
成員初始化列表
- 只能用于構(gòu)造函數(shù)
- 必須用來初始化非靜態(tài)const成員數(shù)據(jù)
- 必須用來初始化引用類型的成員數(shù)據(jù)
- 格式:
Classy:Classy(int n, int m) : mem1(n), mem2(0), mem3(n*m + 2) { // ... }
-
不同作用域的對象構(gòu)造函數(shù)的調(diào)用順序
- 全局對象在main函數(shù)之前構(gòu)造
- 其余的根據(jù)程序的執(zhí)行順序進行構(gòu)造
- 在函數(shù)體內(nèi)部的static對象,只構(gòu)造一次
-
內(nèi)部有其他類的對象的類構(gòu)造順序
- 根據(jù)在類內(nèi)部出現(xiàn)的順序進行構(gòu)造,最后調(diào)用當前類的構(gòu)造函數(shù)。默認調(diào)用的是這些內(nèi)部對象的無參數(shù)的構(gòu)造函數(shù)
- 如果需要調(diào)用內(nèi)部對象的有參數(shù)的構(gòu)造函數(shù),需要在當前類對應(yīng)的構(gòu)造函數(shù)上標明
-
內(nèi)部有其他類的對象的類析構(gòu)順序:跟上面構(gòu)造的順序相反
-
類內(nèi)部的static類型的成員屬于整個類,然后需要使用類名::靜態(tài)成員來訪問
- 為了不創(chuàng)建對象也可以訪問靜態(tài)變量,需要在類外面定義:
int Person::cnt = 0; int Person::get_count(void) { return cnt; } class Person { private: static int cnt ; public: static get_count(void); };- 在類的靜態(tài)函數(shù)里面不能訪問屬于對象的成員,因為后者是非靜態(tài)的,不能確定是那個對象
- 在類的靜態(tài)函數(shù)里可以直接訪問類的靜態(tài)變量,不用在前面加類名
-
友元函數(shù):如果函數(shù)func在類A里面被聲明為friend,那么func可以訪問A的private成員
class Person{
private:
int age;
public:
friend Person add (Person &per1, Person &per2);
};
Person add(Person &per1, Person &per2) {
Person p;
p.age = per1.age + per2.age;
return p;
};
- 注意:友元函數(shù)并不是類的成員函數(shù)
-
操作符重載
- 對加法操作的重載
class Person{ private: int age; public: friend Person operator+ (Person &per1, Person &per2); }; Person operator+(Person &per1, Person &per2) { Person p; p.age = per1.age + per2.age; return p; };- 對++i的重載
class Person{ private: int age; public: friend Person operator++ (Person &per1); }; Person operator++(Person &per1) { per1.age++; return per1; }; // 或者 Person &operator++(Person &per1) { per1.age++; return per1; };- 對i++的重載
class Person{ private: int age; public: friend Person operator++ (Person &per1, int a); }; Person operator++(Person &per1, int a) { Person tmp; tmp = per1; per1.age++; return tmp; };- 對輸出<<的重載
ostream& operator<<(ostream &o, Person p) { cout<<"age: "<<p.age; return o; } -
繼承:class A : public B
- 從一個類派生出另一個類時,原始類稱為基類,繼承類稱為派生類
- public、protected和private類型的成員的區(qū)別
- 不能直接拿父親的私房錢:派生類不能直接訪問基類的private成員
- 可以問父親要錢:需要通過基類的protected/public成員函數(shù)來訪問基類的private成員
- 兒子總是比外人親:派生類可以直接訪問基類的protected成員,但是其他代碼不可以,這里說的派生類可以訪問是指在派生類的成員函數(shù)中可以訪問
- public的成員,外界可以直接訪問
- 在派生類中可以調(diào)整從基類繼承過來的成員的權(quán)限(調(diào)高或者調(diào)低),前提是派生類可以看到基類的成員,比如基類的private成員,在派生類中就看不到
- public、protected和private在繼承方面的區(qū)別

- 復寫
- 派生類有跟基類相同的成員,名字和參數(shù)都一樣
- 多重繼承:繼承多個基類, class A: public B, public C
- 如果不寫public的話,默認是private繼承
- 如果這些基類中有同名的成員,在派生類中訪問會存在二義性,解決方法有2種:
- 在派生類訪問時指明用的是哪個基類的成員:
a.B::func(); - 將同名的成員單獨抽象出來,消除同名的成員,單獨抽象出來的基類通過虛擬繼承來實現(xiàn):
class A: virtual public B {};
- 在派生類訪問時指明用的是哪個基類的成員:
- 盡量避免使用多重繼承
- 構(gòu)造順序:
- 先調(diào)用基類的構(gòu)造:多個基類之間根據(jù)繼承的順序構(gòu)造,從左到右
- 先調(diào)用虛擬基類
- 后一般基類
- 自身:
- 對象成員的構(gòu)造(類的某些成員是其他類的對象)
- 自己的構(gòu)造函數(shù)
- 先調(diào)用基類的構(gòu)造:多個基類之間根據(jù)繼承的順序構(gòu)造,從左到右
-
多態(tài):相同的調(diào)用方法調(diào)用的是不同的類里的成員函數(shù)
- 虛函數(shù):
class Person { private: int age; public: virtual int get_age(void) {return age;} }; - 在使用指針和引用來使用對象時,才會有多態(tài)
- 只有類的成員函數(shù)才能聲明為虛函數(shù)
- 靜態(tài)成員函數(shù)不能是虛函數(shù)
- 內(nèi)聯(lián)函數(shù)不能是虛函數(shù)
- 構(gòu)造函數(shù)不能是虛函數(shù)
- 析構(gòu)函數(shù)一般都聲明為虛函數(shù)
- 重載的函數(shù)不能是虛函數(shù),重載是名字相同,但是參數(shù)不同
- 但是如果返回值是當前類的對象的指針或者引用時可以設(shè)置為虛函數(shù)
- 復寫:函數(shù)參數(shù)、返回值相同,可以設(shè)為虛函數(shù)
- 靜態(tài)聯(lián)編:非虛函數(shù),在編譯時確定好調(diào)用哪個
- 動態(tài)聯(lián)編:對象里面有指針指向虛函數(shù)表,通過指針找到虛函數(shù)表,調(diào)用其中的函數(shù)
- 虛函數(shù):
-
類型轉(zhuǎn)換:xxx_cast<type_id> (expression)
- reinterpret_cast<>: 相當于C風格的使用小括號的強制類型轉(zhuǎn)換,這種方式無法去掉const屬性
- const_cast<>:用于去掉const屬性
- dynamic_cast<>: 動態(tài)類型轉(zhuǎn)換,type_id必須是類的指針、類的引用或者void *
- 如果type_id是類指針的引用,那么expression也必須是一個指針
- 如果type_id是一個引用,那么expression也必須是一個引用
- 用于多態(tài)場合,即:必須有虛函數(shù)
- 有可能轉(zhuǎn)換成功,也有可能轉(zhuǎn)換失敗,比強制類型轉(zhuǎn)換更加安全
- static_cast<>: 靜態(tài)類型轉(zhuǎn)換,編譯的時候檢查是否可以轉(zhuǎn)換成功
- 上行轉(zhuǎn)換安全
- 下行轉(zhuǎn)換不安全
- 上行轉(zhuǎn)換:把派生類的對象轉(zhuǎn)換為基類的對象
- 下行轉(zhuǎn)換:把基類的對象轉(zhuǎn)換為派生類的對象
-
抽象類:含有純虛函數(shù)的類,抽象類不能實例化對象,抽象類用于向派生類定義框架,不提供實現(xiàn),向上提供統(tǒng)一的接口
- 純虛函數(shù):虛函數(shù)后面跟“=0”,如下所示:
class Person{ public: virtual int get_age(void) = 0; // 不提供實現(xiàn),只提供框架接口 };- 派生類如果沒有全部復寫完基類的純虛函數(shù),那么這個派生類也是抽象類
- 析構(gòu)函數(shù)不應(yīng)該使用純虛函數(shù)
-
函數(shù)模板
template<typename T>
T& mymax(T& a, T& b)
{
cout<<__PRETTY_FUNCTION__<<endl;
return (a < b)? b : a;
}
int main(int argc, char **argv)
{
int ia = 1, ib = 2;
float fa = 1, fb = 2;
double da = 1, db = 2;
mymax(ia, ib);
mymax(fa, fb);
mymax(da, db);
return 0;
}
- 類模板
template<typename T>
class AAA {
private:
T t;
public:
void test_func(const T &t);
void print(void);
};
template<typename T> void AAA<T>::test_func(const T &t)
{
this->t = t;
}
template<typename T>
void AAA<T>::print(void)
{
cout<<t<<endl;
}
int main(int argc, char **argv)
{
AAA<int> a;
a.test_func(1);
a.print();
AAA<double> b;
b.test_func(1.23);
b.print();
return 0;
}
- 類模板的重寫
```c++
template<typename T>
class AAA {
private:
T t;
public:
void test_func(const T &t);
void print(void);
};
template<typename T> void AAA<T>::test_func(const T &t)
{
this->t = t;
}
template<typename T>
void AAA<T>::print(void)
{
cout<<t<<endl;
}
template<>
class AAA<int> {
public:
void test_func_int(const int & t)
{
cout<<t<<endl;
}
void print_int(void);
};
void AAA<int>::print_int(void)
{
cout<<"for test"<<endl;
}
int main(int argc, char **argv)
{
AAA<int> a;
a.test_func_int(1);
a.print_int();
AAA<double> b;
b.test_func(1.23);
b.print();
return 0;
}
```
本文來自博客園,作者:dolinux,未經(jīng)同意,禁止轉(zhuǎn)載

浙公網(wǎng)安備 33010602011771號