繼承
父類中默認(rèn)構(gòu)造,析構(gòu),拷貝構(gòu)造operator=是不會(huì)被子類繼承下去的
- 繼承方式
- public:公有繼承
- private:私有繼承
- protected:保護(hù)繼承

如圖可知,繼承方式會(huì)把父類的屬性進(jìn)行權(quán)限降級(jí);public是不變,protected會(huì)把父類除了私有的全部變?yōu)閜rotected,private會(huì)把全部變成private;而且父類私有的不論怎么繼承字類都不能訪問
class 派生類名:繼承方式 基類名{
}
- 繼承中的對象模型
#include <iostream>
using namespace std;
#include <cstring>
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son : public Base
{
public:
int m_D;
};
main()
{
//父類私有屬性雖然子類訪問不到,但是還是被繼承下去了;只是編譯器給隱藏了
cout<<sizeof(Son)<<endl;//16
return 0;
}
- 繼承中的構(gòu)造和析構(gòu)順序
#include <iostream>
using namespace std;
#include <cstring>
class Base
{
public:
Base()
{
cout << "Base的構(gòu)造函數(shù)" << endl;
}
~Base()
{
cout << "Base的析構(gòu)函數(shù)" << endl;
}
};
class Son : public Base
{
public:
Son()
{
cout << "Son的構(gòu)造函數(shù)" << endl;
}
~Son()
{
cout << "Son的析構(gòu)函數(shù)" << endl;
}
};
main()
{
Son s;
//調(diào)用順序:
// Base的構(gòu)造函數(shù)
// Son的構(gòu)造函數(shù)
// Son的析構(gòu)函數(shù)
// Base的析構(gòu)函數(shù)
return 0;
}
#include <iostream>
using namespace std;
#include <cstring>
class Base
{
public:
Base()
{
cout << "Base的構(gòu)造函數(shù)" << endl;
}
~Base()
{
cout << "Base的析構(gòu)函數(shù)" << endl;
}
};
class Other
{
public:
Other()
{
cout << "Other的構(gòu)造函數(shù)" << endl;
}
~Other()
{
cout << "Other的析構(gòu)函數(shù)" << endl;
}
};
class Son : public Base
{
public:
Son()
{
cout << "Son的構(gòu)造函數(shù)" << endl;
}
~Son()
{
cout << "Son的析構(gòu)函數(shù)" << endl;
}
Other other;
};
main()
{
Son s;
//調(diào)用順序:
// Base的構(gòu)造函數(shù)
// Other的構(gòu)造函數(shù)
// Son的構(gòu)造函數(shù)
// Son的析構(gòu)函數(shù)
// Other的析構(gòu)函數(shù)
// Base的析構(gòu)函數(shù)
return 0;
}
#include <iostream>
using namespace std;
#include <cstring>
class Base
{
public:
Base(int a)
{
cout << "Base的構(gòu)造函數(shù)" << endl;
}
};
class Son : public Base
{
public:
// Son(int a=100):Base(a)
Son(int a):Base(a)//利用初始化列表語法,顯示調(diào)用父類中的其他構(gòu)造函數(shù)
{
cout << "Son的構(gòu)造函數(shù)" << endl;
}
};
main()
{
// Son s;
Son s(10);
return 0;
}
繼承中同名成員的處理
#include <iostream>
using namespace std;
#include <cstring>
class Base
{
public:
Base()
{
this->m_A=10;
}
void func(){
}
void func(int a){
}
int m_A;
};
class Son : public Base
{
public:
Son()
{
this->m_A=100;
}
void func(){
}
int m_A;
};
main()
{
Son s;
//同名就近
cout<<s.m_A<<endl;//100
//訪問父類的成員
cout<<s.Base::m_A<<endl;//10
//同理:同名成員函數(shù)也是如此
//當(dāng)子類重新定義了父類中的同名成員函數(shù),子類的成員函數(shù)會(huì)
//隱藏掉父類中的所有重載版本的同名成員,可以利用作用域顯示的指定調(diào)用
//注意:只是隱藏,不是干掉了
// s.func(10); //錯(cuò)誤
s.Base::func(10);//正確
return 0;
}
- 繼承中的同名靜態(tài)成員
#include <iostream>
using namespace std;
#include <cstring>
class Base
{
public:
Base()
{
}
static void func()
{
}
static int m_A;
};
int Base::m_A = 0;
class Son : public Base
{
public:
Son()
{
}
static void func()
{
}
static int m_A;
};
int Son::m_A = 100;
main()
{
Son s;
//1. 通過對象訪問
cout << s.m_A << endl; //100
cout << s.Base::m_A << endl; //0
//2. 通過類名訪問
cout << Son::m_A << endl; //100
cout << Son::Base::m_A << endl; //0
//靜態(tài)函數(shù)調(diào)用
s.func();
s.Base::func();
Son::func();
Son::Base::func();
// /當(dāng)子類重新定義了父類中的同名成員函數(shù),子類的成員函數(shù)會(huì)
//隱藏掉父類中的所有重載版本的同名成員,可以利用作用域顯示的指定調(diào)用
// Son::Base::func(1);//參考成員函數(shù)的處理,一摸一樣
return 0;
}
多繼承
#include <iostream>
using namespace std;
#include <cstring>
class Base
{
public:
Base()
{
}
};
class Base1
{
public:
Base1()
{
}
};
class Son : public Base, public Base1
{
public:
Son()
{
}
};
main()
{
Son s;
//多繼承如果同名也是通過作用域去精確調(diào)用
return 0;
}
菱形繼承
兩個(gè)派生類繼承同一個(gè)基類而又有某個(gè)類同時(shí)繼承著兩個(gè)派生類,這種繼承被稱為菱形繼承,或者鉆石型繼承
- 虛繼承(重要)
避免了內(nèi)存浪費(fèi)和定義不明確
2.png
#include <iostream>
using namespace std;
#include <cstring>
class Animal
{
public:
int m_Age;
};
//Animal稱為虛基類
class Sheep:virtual public Animal{};
class Tuo:virtual public Animal{};
class SheepTuo:public Sheep,public Tuo{};
main()
{
SheepTuo s;
s.Sheep::m_Age=10;
s.Tuo::m_Age=20;
cout<< s.Sheep::m_Age<<endl;//20
cout<< s.Tuo::m_Age<<endl;//20
cout<< s.m_Age<<endl;//20
//如果不加virtual則,上面三個(gè)分別是10 20和無法識(shí)別
//加了之后則m_Age只在Animal中一份,其他幾個(gè)是沒有的
//Sheep/Tuo有個(gè)vbpter(虛基類指針)指向vbtable里面有個(gè)偏移量(兩個(gè)類偏移量也不同)
//通過偏移量就可以找到內(nèi)存中唯一一份Animal中的m_Age;其實(shí)就是通過地址尋找
return 0;
}
多態(tài)
c++支持編譯時(shí)多態(tài)(靜態(tài)多態(tài))和運(yùn)行時(shí)多態(tài)(動(dòng)態(tài)多態(tài)),運(yùn)算符重載和函數(shù)重載就是編譯時(shí)多態(tài),而派生類和虛函數(shù)實(shí)現(xiàn)是運(yùn)行時(shí)多態(tài)
靜態(tài)多態(tài)和動(dòng)態(tài)多態(tài)的區(qū)別就是函數(shù)地址是早綁定(靜態(tài)聯(lián)編)還是晚綁定(動(dòng)態(tài)聯(lián)編)。如果函數(shù)的調(diào)用再編譯階段就能確定函數(shù)的調(diào)用地址,并產(chǎn)生代碼就是靜態(tài)多態(tài)。而如果函數(shù)的調(diào)用地址不能編譯不能在編譯期間確定,而需要在運(yùn)行時(shí)才能決定,這就屬于晚綁定(運(yùn)行時(shí)多態(tài))
- 動(dòng)態(tài)多態(tài)產(chǎn)生條件
- 先有繼承關(guān)系
- 父類有虛函數(shù)
- 子類重寫父類虛函數(shù)
- 父類的指針或引用,指向子類對象
#include <iostream>
using namespace std;
#include <cstring>
class Animal
{
public:
virtual void speak()
{
cout << "動(dòng)物在說話" << endl;
}
};
class Cat : public Animal
{
public:
void speak()
{
cout << "小貓?jiān)谡f話" << endl;
}
};
//對于有父子類關(guān)系的類,指針或者引用是可以直接轉(zhuǎn)換的
void doSpeak(Animal &animal)
{
// animal.speak();//動(dòng)物在說話 地址早綁定,屬于靜態(tài)聯(lián)編
//如果想調(diào)用小貓說話,地址就不能早綁定;需要?jiǎng)討B(tài)聯(lián)編
//只需要在Animal的speak函數(shù)前加 ,就成為虛函數(shù)就行了
animal.speak();//小貓?jiān)谡f話
}
main()
{
Cat cat;
doSpeak(cat);
return 0;
}
-
虛函數(shù)原理
1.png
父類有虛函數(shù)之后,本質(zhì)發(fā)生了變化,當(dāng)父類指向子類調(diào)用子類函數(shù)時(shí)候,
實(shí)際上通過虛函數(shù)表指針指針虛函數(shù)表,調(diào)用的就是實(shí)際的子類對象的函數(shù)
實(shí)際上就是通過偏移量去調(diào)用函數(shù)
- 純虛函數(shù)和抽象類
抽象類無法實(shí)例化
class Animal
{
public:
//純虛函數(shù)
//如果一個(gè)類中包含了純虛函數(shù),那么這個(gè)類就無法實(shí)例化對象了
//這個(gè)類我們通常稱為抽象類
//抽象類的子類,必須重寫父類中的純虛函數(shù),否則也屬于抽象類
virtual void getReuslt()=0;
};
- 虛析構(gòu)和純虛析構(gòu)
- 純虛函數(shù)是沒有實(shí)現(xiàn)的,
純虛析構(gòu)類內(nèi)聲明類外實(shí)現(xiàn) - 如果一個(gè)類有個(gè)
純虛析構(gòu)函數(shù),那么也是抽象類,無法實(shí)例化對象;但是子類不需要重寫(和純虛函數(shù)的區(qū)別)
- 純虛函數(shù)是沒有實(shí)現(xiàn)的,
#include <iostream>
using namespace std;
#include <cstring>
class Animal
{
public:
Animal()
{
cout << "Animal構(gòu)造" << endl;
}
//虛析構(gòu)
//如果子類中有指向堆區(qū)的屬性,那么要利用虛析構(gòu)技術(shù)再delete的時(shí)候調(diào)用子類的析構(gòu)函數(shù)
virtual ~Animal()
{
cout << "Animal析構(gòu)" << endl;
}
//析構(gòu)不能重載不能有多個(gè),一般只寫一個(gè)
//純虛析構(gòu):需要有聲明也需要有實(shí)現(xiàn);類內(nèi)聲明,類外實(shí)現(xiàn)
// virtual ~Animal() = 0;
virtual void speak()
{
cout << "動(dòng)物叫" << endl;
}
};
// Animal::~Animal()
// {
// cout << "Animal純虛析構(gòu)的調(diào)用" << endl;
// }
class Cat : public Animal
{
public:
Cat(char *name)
{
cout << "Cat構(gòu)造" << endl;
this->m_Name = new char[strlen(name) + 1];
strcpy(this->m_Name, name);
}
virtual void speak()
{
cout << this->m_Name << "貓叫" << endl;
}
~Cat()
{
cout << "Cat析構(gòu)" << endl;
if (this->m_Name)
{
delete[] this->m_Name;
this->m_Name = NULL;
}
}
char *m_Name;
};
main()
{
//多態(tài)形式把子類屬性創(chuàng)建再堆區(qū)的
//那么父類析構(gòu)不會(huì)調(diào)用,需要把父類析構(gòu)也加上virtual才會(huì)被調(diào)用
Animal *a = new Cat("Tom");
a->speak();
delete a;
//有純虛析構(gòu),也是抽象類無法實(shí)例化
// Animal aa;
return 0;
}
// Animal構(gòu)造
// Cat構(gòu)造
// Tom貓叫
// Cat析構(gòu)
// Animal析構(gòu)
- 向上向下類型轉(zhuǎn)換

- 因?yàn)樽宇惪赡苡袛U(kuò)展屬性/函數(shù),所以內(nèi)存指針范圍更大;那么怎么轉(zhuǎn)換才安全呢?
- 左邊父右邊子(子轉(zhuǎn)父)
- 最開始new的就是子,最終怎么轉(zhuǎn)都是正常的
重寫,重載,重定義
- 重載:同一個(gè)作用域的同名函數(shù)
- 同一個(gè)作用域
- 參數(shù)個(gè)數(shù),參數(shù)順序,參數(shù)類型不同
- 和函數(shù)返回值無關(guān)
- const也可以作為重載條件 //do(const T t) do(T t)
- 重定義(隱藏)/c++中重定義類似java中重寫
- 有繼承
- 子類重新定義父類的同名成員(非virtual函數(shù))
- 重寫(覆蓋)
- 有繼承
- 子類重寫父類的virtual函數(shù)
- 函數(shù)返回值,函數(shù)名字,函數(shù)參數(shù),必須和基類中的虛函數(shù)一致
泛型編程
模板
c++提供兩種模板機(jī)制:函數(shù)模板和類模板
函數(shù)模板
#include <iostream>
using namespace std;
#include <cstring>
//利用模板實(shí)現(xiàn)通用交換函數(shù):而且緊跟著的函數(shù)/類才能使用當(dāng)前模板,后面函數(shù)就需要重新書寫模板了
template <typename T>
void mySwap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
//模板不能單獨(dú)使用,必須指定出T才能使用
template <typename T>
void mySwap2()
{
T a;
}
int main()
{
int a = 10;
int b = 20;
//1. 自動(dòng)類型推導(dǎo)
mySwap(a, b);
cout << a << b << endl; //2010
//2.顯示指定類型
mySwap<int>(a, b);
cout << a << b << endl; //1020
//雖然沒有參數(shù),但是T在函數(shù)內(nèi)部已經(jīng)存在,沒有指定類型也沒有自動(dòng)推導(dǎo),所以內(nèi)存怎么分配呢,所以不能這樣使用
// mySwap2();//錯(cuò)誤
//模板不能單獨(dú)使用,必須指定出T才能使用
mySwap2<int>();//可以
return 0;
}
- 通用排序函數(shù),實(shí)現(xiàn)對char和int數(shù)組的排序
#include <iostream>
using namespace std;
#include <cstring>
template <typename T>
void mySwap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
//通用排序函數(shù),實(shí)現(xiàn)對char和int數(shù)組的排序
template <class T> //typename和class效果完全一樣;
void mySort(T arr[], int len)
{
for (int i = 0; i < len; i++)
{
int max = i;
for (int j = i + 1; j < len; j++)
{
if (arr[max] < arr[j])
{
max = j;
}
}
//判斷算出的max和開始認(rèn)定的i是否一致,如果不同則交換數(shù)據(jù)
if (i != max)
{
mySwap(arr[i], arr[max]);
}
}
}
template <class T>
void printArray(T arr[], int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << endl;
}
}
void test01()
{
// char charArray[] = "helloworld";
// int len = strlen(charArray);
// mySort(charArray, len);
// printArray(charArray, len);
int intArray[] = {5, 7, 1, 4, 2, 3};
int len = sizeof(intArray)/sizeof(int);
mySort(intArray, len);
printArray(intArray, len);
}
int main()
{
test01();
return 0;
}
- 函數(shù)模板和普通函數(shù)的區(qū)別以及調(diào)用規(guī)則
#include <iostream>
using namespace std;
#include <cstring>
template <class T>
T myAdd(T a, T b)
{
return a + b;
}
int myAdd2(int a, int b)
{
return a + b;
}
void test01()
{
int a = 10;
int b = 20;
char c = 'c';
// myAdd(a,c);//如果使用自動(dòng)類型推導(dǎo),是不會(huì)發(fā)生隱式類型轉(zhuǎn)換的,會(huì)報(bào)錯(cuò)
myAdd2(a, b); //普通函數(shù)會(huì)發(fā)生普通類型轉(zhuǎn)換
myAdd<int>(a, b); //指定類型,可以進(jìn)行隱式類型轉(zhuǎn)換
}
//2. 函數(shù)模板和普通函數(shù)的調(diào)用規(guī)則
template <class T>
void myPrint(T a, T b)
{
cout << "函數(shù)模板調(diào)用" << endl;
}
template <class T>
void myPrint(T a, T b,T c)
{
cout << "函數(shù)模板三個(gè)參數(shù)調(diào)用" << endl;
}
void myPrint(int a, int b)
{
cout << "普通函數(shù)調(diào)用" << endl;
}
void test02()
{
int a = 10;
int b = 20;
//1. 如果函數(shù)模板和普通函數(shù)都可以調(diào)用,優(yōu)先調(diào)用普通函數(shù),因?yàn)樾阅芨? myPrint(a, b); //普通函數(shù)調(diào)用
//2. 如果強(qiáng)制調(diào)用函數(shù)模板,可以使用空模板參數(shù)列表
myPrint<>(a, b);//函數(shù)模板調(diào)用
//3. 函數(shù)模板也可以發(fā)生函數(shù)重載
myPrint<>(a, b,10);//函數(shù)模板三個(gè)參數(shù)調(diào)用
//4. 如果函數(shù)模板能產(chǎn)生更好匹配,優(yōu)先使用函數(shù)模板
//例如:此時(shí)如果是普通函數(shù),還要char轉(zhuǎn)int,所以就不是更好的匹配,所以優(yōu)先使用函數(shù)模板
char c='c';
char d='d';
myPrint(c,d);//函數(shù)模板調(diào)用
}
int main()
{
test02();
return 0;
}
- 模板機(jī)制和模板局限性
- 模板機(jī)制
- 編譯器并不是把函數(shù)模板處理成能夠處理任何類型的函數(shù),例如自定義類型
- 函數(shù)模板通過具體類型產(chǎn)生不同的函數(shù),生成的函數(shù)成為模板函數(shù)
- 編譯器會(huì)對函數(shù)模板進(jìn)行二次編譯,在聲明的地方對模板代碼本身進(jìn)行編譯,在調(diào)用的地方對參數(shù)替換后的代碼進(jìn)行編譯
- 模板局限性
編寫的模板函數(shù)很可能無法處理某些類型,例如T是Person怎么> =比較;另一方面,有時(shí)候通用化是有意義的,但C++語法不允許這樣做。
為了解決這種問題,可以提供模板的重載,為這些特定的類型提供具體化的模板
- 模板機(jī)制
//利用具體換技術(shù)實(shí)現(xiàn),或者也可以通過運(yùn)算符重載實(shí)現(xiàn)
template <>bool myCompare(Person &a, Person &b)
{
if (a.name==b.name) return true
}
類模板
- 基本語法
#include <iostream>
using namespace std;
#include <cstring>
template <class T, class D=int> //可以有默認(rèn)值,則下面就可以不指定這個(gè)
class Person
{
public:
Person(T name, D age)
{
this->name = name;
this->age = age;
}
T name;
D age;
};
void test01()
{
//自動(dòng)類型推導(dǎo):類模板不可以使用類型推導(dǎo)
// Person p1("John",100);//錯(cuò)誤
//正確:顯示指定類型
Person<string> p1("John", 1);
}
int main()
{
test01();
return 0;
}
- 成員函數(shù)創(chuàng)建時(shí)機(jī)
#include <iostream>
using namespace std;
#include <cstring>
class Person1
{
public:
void showPerson1()
{
cout << "person1 show 調(diào)用" << endl;
}
};
class Person2
{
public:
void showPerson2()
{
cout << "person2 show 調(diào)用" << endl;
}
};
template <class T>
class MyClass
{
public:
void func1()
{
obj.showPerson1();
}
void func2()
{
obj.showPerson2();
}
T obj;
};
void test01()
{
MyClass<Person1> p1;
p1.func1();
//類模板中的成員函數(shù)并不是一開始創(chuàng)建出來的,而是運(yùn)行階段確定出T的數(shù)據(jù)類型才創(chuàng)建的
// p1.func2();//此處是調(diào)用失敗的
MyClass<Person2> p1;
p1.func2();
}
int main()
{
test01();
return 0;
}
- 類模板做函數(shù)參數(shù)
#include <iostream>
using namespace std;
#include <cstring>
template <class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
void shwoPerson()
{
cout << "姓名:" << this->name << "年齡:" << this->age << endl;
}
T1 name;
T2 age;
};
//1. 指定傳入的類型
void doWork(Person<string,int> &p){
p.shwoPerson();
}
//2. 參數(shù)模板化
template<class T1, class T2>
void doWork2(Person<T1,T2> &p){
p.shwoPerson();
}
//3.整個(gè)類模板化
template<class T>
void doWork3(T &p){
p.shwoPerson();
}
void test01()
{
Person<string,int> p("Hello",999);
doWork(p);
//因?yàn)镻erson<string,int>會(huì)把類型傳遞到模板上template<class T1, class T2>,則doWork2自然知道類型
doWork2(p);
doWork3(p);
}
int main()
{
test01();
return 0;
}
- 類模板的繼承的寫法
#include <iostream>
using namespace std;
#include <cstring>
//形式一:寫死了,不推薦
template <class T>
class Base
{
public:
T m_A;
};
//指定類型,父類才能知道T類型,然后才能給子類分配內(nèi)存
class Son : public Base<int>
{
};
//形式二
template <class T>
class Base1
{
public:
T m_A;
};
template <class T1, class T2>
class Son1 : public Base1<T2>
{
public:
T1 m_B;
};
void test01()
{
Son1<int,double>s;
}
int main()
{
test01();
return 0;
}
- 類模板中成員函數(shù)類外實(shí)現(xiàn)
#include <iostream>
using namespace std;
#include <cstring>
template <class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age);
// {
// this->m_A=name;
// this->m_B=age;
// }
void showPerson();
T1 m_A;
T2 m_B;
};
template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_A = name;
this->m_B = age;
}
template <class T1, class T2>
void Person<T1, T2>::showPerson()
{
}
void test01()
{
Person<string, int> p("hello", 100);
}
int main()
{
test01();
return 0;
}
- 類模板中的友元
- 類內(nèi)實(shí)現(xiàn)
#include <iostream>
using namespace std;
#include <cstring>
template <class T1, class T2>
class Person
{
public:
//友元函數(shù),類內(nèi)的實(shí)現(xiàn);其實(shí)本質(zhì)上還是全局函數(shù),所以下面是直接調(diào)用
friend void printPerson(Person<T1, T2> &p)
{
cout << p.m_A << endl;
}
Person(T1 name, T2 age)
{
this->m_A = name;
this->m_B = age;
}
private:
T1 m_A;
T2 m_B;
};
void test01()
{
Person<string, int> p("John", 12);
printPerson(p);
}
int main()
{
test01();
return 0;
}
- 類外實(shí)現(xiàn)(較復(fù)雜)
//方式一:略復(fù)雜
#include <iostream>
using namespace std;
#include <cstring>
//函數(shù)模板的聲明
template <class T1, class T2>
class Person;
//針對類外實(shí)現(xiàn)需要提前聲明,又因?yàn)檫@里用到了Person,所以也需要把person聲明提前告訴編譯器
template <class T1, class T2>
void printPerson1(Person<T1, T2> &p);
template <class T1, class T2>
class Person
{
public:
//友元函數(shù),類外實(shí)現(xiàn);加上<>其實(shí)就是告訴編譯器,是模板函數(shù)而不是普通函數(shù),和外面對應(yīng)起來,不然編譯器當(dāng)成普通函數(shù)是找不到類外的實(shí)現(xiàn)
//因?yàn)轭愅馐悄0搴瘮?shù)實(shí)現(xiàn)
friend void printPerson1<>(Person<T1, T2> &p);
Person(T1 name, T2 age)
{
this->m_A = name;
this->m_B = age;
}
private:
T1 m_A;
T2 m_B;
};
template <class T1, class T2>
void printPerson1(Person<T1, T2> &p)
{
cout <<"類外"<< p.m_A << endl;
}
void test01()
{
Person<string, int> p("John", 12);
printPerson1(p);
}
int main()
{
test01();
return 0;
}
//方式二:簡單一點(diǎn)
#include <iostream>
using namespace std;
#include <cstring>
template <class T1, class T2>
class Person;
//聲明實(shí)現(xiàn)在一起
template <class T1, class T2>
void printPerson1(Person<T1, T2> &p)
{
cout << "類外" << p.m_A << endl;
}
template <class T1, class T2>
class Person
{
public:
friend void printPerson1<>(Person<T1, T2> &p);
Person(T1 name, T2 age)
{
this->m_A = name;
this->m_B = age;
}
private:
T1 m_A;
T2 m_B;
};
void test01()
{
Person<string, int> p("John", 12);
printPerson1(p);
}
int main()
{
test01();
return 0;
}
類型轉(zhuǎn)換
- 靜態(tài)轉(zhuǎn)換
#include <iostream>
using namespace std;
#include <cstring>
//1. 靜態(tài)類型轉(zhuǎn)換
void test01()
{
//允許內(nèi)置數(shù)據(jù)類型之間的轉(zhuǎn)換
char a = 'a';
double d = static_cast<double>(a);
cout << d << endl; //97
}
class Base
{
};
class Son : public Base
{
};
class Other
{
};
void test02()
{
Base *base = NULL;
Son *son = NULL;
//將base轉(zhuǎn)son,父轉(zhuǎn)子,向下類型轉(zhuǎn)換,不安全
Son *son2 = static_cast<Son *>(base);
//子轉(zhuǎn)父
Base *base2=static_cast<Base *>(son);
//base轉(zhuǎn)為other:無效且錯(cuò)誤,這種轉(zhuǎn)換必須有父子關(guān)系
Other *other=static_cast<Other *>(base);
}
int main()
{
test01();
return 0;
}
- 動(dòng)態(tài)轉(zhuǎn)換
具有類型檢查的功能,比靜態(tài)轉(zhuǎn)換更安全
#include <iostream>
using namespace std;
#include <cstring>
void test01()
{
//不允許內(nèi)置數(shù)據(jù)類型之間的轉(zhuǎn)換;因?yàn)榭赡芫葋G失
// char a = 'a';
// double d = dynamic_cast<double>(a);
// cout << d << endl;
}
class Base
{
};
class Son : public Base
{
};
class Other
{
};
void test02()
{
Base *base = NULL;
Son *son = NULL;
// Son *son2 = dynamic_cast<Son *>(base); //不安全不允許
Base *base2 = dynamic_cast<Base *>(son); //安全是允許的
// Other *other = dynamic_cast<Other *>(base); //無關(guān)系,不允許
}
//多態(tài)相關(guān)轉(zhuǎn)換
class Base1
{
virtual void func() {}
};
class Son1 : public Base1
{
void func() {}
};
void test03()
{
//如果發(fā)生多態(tài),那么轉(zhuǎn)換總是安全的:如下寫法
Base1 *base = new Son1;
Son1 *son = NULL;
Son1 *son1 = dynamic_cast<Son1 *>(base);
}
int main()
{
test01();
return 0;
}
常量轉(zhuǎn)換
不能直接對非指針和非引用的變量使用
const_cast操作符去直接移除它的const
void test01()
{
const int *p=NULL;
int *pp=const_cast<int*>(p);
const int *ppp=const_cast<const int*>(pp);
}
int main()
{
test01();
return 0;
}
重新解釋轉(zhuǎn)換
是最不安全的一種轉(zhuǎn)換機(jī)制,主要用于將一種數(shù)據(jù)類型從一種類型轉(zhuǎn)化為另一種類型。它可以將一個(gè)指針轉(zhuǎn)換成一個(gè)整數(shù),也可以將一個(gè)整數(shù)轉(zhuǎn)換成指針
int a=10;
int *p=reinterpret_cast<int*>(a);
異常
如果多層捕獲異常,默認(rèn)是最里面的捕獲,但是可以在子異常中直接寫
throw來向上傳遞
異常必須有函數(shù)進(jìn)行處理,如果不處理,程序會(huì)自動(dòng)調(diào)用terminate函數(shù),讓程序崩潰中斷
catch(...){
throw;
}
#include <iostream>
using namespace std;
#include <cstring>
class MyException
{
public:
void printError()
{
cout << "自定義異常" << endl;
}
}
int
myDiv(int a, int b)
{
if (b == 0)
{
// return -1;
// throw -1; //返回int類型的異常,而不是-1
// throw 'c'; //返回char類型異常
//棧解旋:可通過p1,p2的構(gòu)造析構(gòu)去看
//從try代碼塊開始,到throw拋出異常之前,所有棧上的數(shù)據(jù)都會(huì)被釋放掉
//釋放的順序和構(gòu)造順序是相反的,這個(gè)過程成為棧解旋
Person p1;
Person p2;
throw new MyException(); //拋出MyException匿名對象
}
return a / b;
}
int main()
{
try
{
myDiv(1, 0);
}
catch (int)
{
cout << "int類型異常捕獲" << endl;
}
catch (char)
{
cout << "char類型異常捕獲" << endl;
}
catch (MyException e)
{
e.printError();
}
catch (...)
{
cout << "其他類型異常捕獲" << endl;
}
return 0;
}
異常的接口聲明
qt和linux下是正確的,vs并沒有提供這種機(jī)制
void func() throw(int)
// void func() throw(...)
{
//只允許拋出int
throw 1;
}
int main()
{
try
{
func();
}
catch (int)
{
}
return 0;
}
- 異常變量的生命周期
#include <iostream>
using namespace std;
#include <cstring>
class MyException
{
public:
MyException()
{
cout << "MyException默認(rèn)構(gòu)造" << endl;
}
MyException(const MyException &e)
{
cout << "MyException拷貝構(gòu)造" << endl;
}
~MyException()
{
cout << "MyException析構(gòu)函數(shù)" << endl;
}
void printError()
{
cout << "自定義異常" << endl;
}
};
void doWork()
{
//調(diào)用默認(rèn)構(gòu)造
throw MyException();
// throw new MyException();//只會(huì)調(diào)用默認(rèn)構(gòu)造,但是需要自己管理釋放內(nèi)存delete
}
int main()
{
try
{
doWork();
}
catch (MyException e) //調(diào)用拷貝構(gòu)造,效率低
// catch (MyException &e) //效率高一些,推薦
{
e.printError();
}
catch (...)
{
cout << "其他類型異常捕獲" << endl;
}
return 0;
}
//打印結(jié)果
// MyException默認(rèn)構(gòu)造
// MyException拷貝構(gòu)造
// 自定義異常
// MyException析構(gòu)函數(shù)
// MyException析構(gòu)函數(shù)
//如果:catch (MyException &e) 傳入引用
// MyException默認(rèn)構(gòu)造
// 自定義異常
// MyException析構(gòu)函數(shù)
- 異常的多態(tài)
#include <iostream>
using namespace std;
#include <cstring>
class BaseException
{
public:
virtual void printError() = 0;
};
class NULLPointException : public BaseException
{
public:
void printError()
{
cout << "NULLPointException異常" << endl;
}
};
void doWork()
{
throw NULLPointException();
}
int main()
{
try
{
doWork();
}
catch (BaseException &e)
{
e.printError();
}
return 0;
}
- 系統(tǒng)標(biāo)準(zhǔn)異常使用
//注意
#include <stdexcept> //異常需要這個(gè)頭文件
void doWork()
{
//系統(tǒng)異常:還有很多其他內(nèi)置系統(tǒng)異常
throw out_of_range("越界異常");
}
int main()
{
try
{
doWork();
}
catch (out_of_range &e)
{
cout<<e.what()<<endl; //越界異常
}
return 0;
}
標(biāo)準(zhǔn)輸入輸出流

- 輸入流
int main()
{
//利用cin.get取出數(shù)據(jù)時(shí)候,換行符會(huì)遺留在緩沖區(qū)中,例如get(buf,1024;之后再次cin.get()才可以取出換行符
// cin.get();//一次只能讀取一個(gè)字符
// cin.get(一個(gè)參數(shù));//讀一個(gè)字符串
// cin.get(兩個(gè)參數(shù));//可以讀字符串 cin.get(buf,1024);
// cin.getline();//不同于get,這個(gè)取出了換行符
// cin.ignore(); //忽略,默認(rèn)忽略一個(gè)字符
// cin.ignore(2); //忽略,忽略前兩個(gè)字符,同理參數(shù)x就代表忽略多少個(gè)字符
// cin.peek();//偷窺,就是只是看看,如果之后繼續(xù)get還是從上次get的開始讀
//cin.fail();//緩沖區(qū)狀態(tài) 0代表正常,1代表異常
// cin.putback();//放回,就是放回原來位置好像沒有進(jìn)行任何操作一樣
char c = cin.get();
cin.putback(c);
char buf[1024] = {0};
cin.getline(buf, 1024);
cout << buf << endl;//最終發(fā)現(xiàn),輸入什么輸出什么,沒有因?yàn)閏in.get()產(chǎn)生任何影響
return 0;
}
- 輸出流
int main()
{
// cout.flush();//刷新緩沖區(qū)linux下有效
// cout.put('h');//向緩沖區(qū)寫字符
// cout.write();//從buffer中寫num個(gè)字節(jié)到當(dāng)前輸出流中
cout.put('h').put('e');
char buf[] = "hello world";
cout.write(buf, strlen(buf));
cout<<"hello world"<<endl;//這才是最常用
//流成員函數(shù)格式化輸出
int num =99;
cout.width(20);//指定寬度為20
cout.fill('*');//填充
cout.setf(ios:left);//左對齊
cout.unsetf(ios:dec);//卸載十進(jìn)制
cout.setf(ios:hex);//安裝十六進(jìn)制
cout.setf(ios:showbase);//顯示基數(shù)
cout.unsetf(ios:hex);//卸載十六進(jìn)制
cout.setf(ios:oct);//安裝八進(jìn)制
//使用控制符格式化輸出
//include <iomanip>
int num1=99;
cout<<setw(20) //設(shè)置寬度
<<setfill('~')//設(shè)置填充
<<setiosflags(ios::showbase) //顯示基數(shù)
<<setiosflags(ios::left)//設(shè)置左對齊
<<hex //顯示十六進(jìn)制
<<number
<<endl;
return 0;
}
讀寫文件
有很多種形式,參考即可
#include <fstream>
#include <iostream>
using namespace std;
int main ()
{
char data[100];
// 以寫模式打開文件
ofstream outfile;
outfile.open("afile.dat");
cout << "Writing to the file" << endl;
cout << "Enter your name: ";
cin.getline(data, 100);
// 向文件寫入用戶輸入的數(shù)據(jù)
outfile << data << endl;
cout << "Enter your age: ";
cin >> data;
cin.ignore();
// 再次向文件寫入用戶輸入的數(shù)據(jù)
outfile << data << endl;
// 關(guān)閉打開的文件
outfile.close();
// 以讀模式打開文件
ifstream infile;
infile.open("afile.dat");
cout << "Reading from the file" << endl;
infile >> data;
// 在屏幕上寫入數(shù)據(jù)
cout << data << endl;
// 再次從文件讀取數(shù)據(jù),并顯示它
infile >> data;
cout << data << endl;
// 關(guān)閉打開的文件
infile.close();
return 0;
}
- 文件位置指針
// 定位到 fileObject 的第 n 個(gè)字節(jié)(假設(shè)是 ios::beg)
fileObject.seekg( n );
// 把文件的讀指針從 fileObject 當(dāng)前位置向后移 n 個(gè)字節(jié)
fileObject.seekg( n, ios::cur );
// 把文件的讀指針從 fileObject 末尾往回移 n 個(gè)字節(jié)
fileObject.seekg( n, ios::end );
// 定位到 fileObject 的末尾
fileObject.seekg( 0, ios::end );

