這兩期講完基本上面試遇到的相關(guān)問題就過了一半了,后續(xù)將STL和內(nèi)存相關(guān)的補(bǔ)充完整,C++這塊的基本上就全部結(jié)束了,以后可能再也不會像現(xiàn)在這樣在這個方向投入過多時間,且行且珍惜啊,還是跟以前一樣,所有的總結(jié)都會有PDF版,如有需要自取。廢話不多說,發(fā)完這期,繼續(xù)整理STL去了。
1、C++函數(shù)模板
- 模板的意義:對類型也可以進(jìn)行參數(shù)化了。
- 函數(shù)模板 《= 是不進(jìn)行編譯的,因?yàn)轭愋筒恢馈?/li>
- 模板的實(shí)例化 《= 函數(shù)調(diào)用點(diǎn)進(jìn)行實(shí)例化。
- 模板函數(shù) 《= 才是被編譯器所編譯的。
- 模板類型參數(shù)。
- 模板非類型參數(shù)。
- 模板的實(shí)參推演 =》 可以根據(jù)用戶傳入的實(shí)參的類型,來推導(dǎo)模板類型。
- 模板的特例化。
- 函數(shù)模板、模板的特例化、非模板函數(shù)的重載關(guān)系。
- 模板代碼是不能在一個文件中定義,在另外一個文件中使用的。
- 模板代碼調(diào)用之前,一定要看到模板定義的地方,這樣的話,模板才能夠進(jìn)行正常的實(shí)例化,產(chǎn)生能夠被編譯器編譯的代碼。
- 所以,模板代碼都是放在頭文件當(dāng)中的,然后在源文件當(dāng)中直接進(jìn)行#includ包含。
2、泛型算法
- 泛型算法參數(shù)接收的都是迭代器!
- 泛型函數(shù) - 全局的函數(shù) - 給所有容器用的
- 泛型函數(shù),有一套方式,,能夠統(tǒng)一的遍歷所有的容器的元素 - 迭代器。
3、拷貝賦值和移動賦值
- 拷貝賦值是通過拷貝構(gòu)造函數(shù)來賦值,在創(chuàng)建對象時,使用同一類中之前創(chuàng)建的對象來初始化新創(chuàng)建的對象。
- 移動賦值是通過移動構(gòu)造函數(shù)來賦值,二者的主要區(qū)別在于:
- 拷貝構(gòu)造函數(shù)的形參是一個左值引用,而移動構(gòu)造函數(shù)的形參是一個右值引用。
- 拷貝構(gòu)造函數(shù)完成的是整個對象或變量的拷貝,而移動構(gòu)造函數(shù)是生成一個指針指向源對象或變量的地址,接管源對象的內(nèi)存,相對于大量數(shù)據(jù)的拷貝節(jié)省時間和內(nèi)存空間。
4、虛函數(shù)、靜態(tài)綁定、動態(tài)綁定
虛函數(shù)表位于只讀數(shù)據(jù)段(.rodata) ,也就是C++內(nèi)存模型中的常量區(qū);而 虛函數(shù)則位于代碼段(.text) ,也就是C++內(nèi)存模型中的代碼區(qū)。
一個類添加了虛函數(shù),對這個類有什么影響?
- 如果類里面定義了虛函數(shù),那么編譯階段,編譯器給這個類類型產(chǎn)生一個唯一的vftable虛函數(shù)表,虛函數(shù)表中主要存儲的內(nèi)容就是RTTI指針和虛函數(shù)的地址。當(dāng)程序運(yùn)行時,每一張?zhí)摫砗瘮?shù)都會加載到內(nèi)存的 .rodata區(qū)。
- 一個類里面定義了虛函數(shù),那么這個類定義的對象,其運(yùn)行時,內(nèi)存中開始部分,多存儲一個vfptr虛函數(shù)指針,指向相應(yīng)類型的虛函數(shù)表vftable。一個類型定義的n個對象,它們的vfptr指向的都是同一張?zhí)摵瘮?shù)表。
- 一個類里面虛函數(shù)的個數(shù),不影響對象內(nèi)存大小(vfptr),影響的是虛函數(shù)表的大小
- 如果派生類中的方法,和基類繼承來的某個方法,返回值、函數(shù)名、參數(shù)列表都相同,而且基類的方法是virtual虛函數(shù),那么派生類的這個方法,自動處理成虛函數(shù)。
靜態(tài)綁定和動態(tài)綁定:綁定指的是函數(shù)調(diào)用
- 靜態(tài)綁定在編譯時期,綁定的是普通函數(shù)的調(diào)用 指令 :call Base::show(地址)
- 動態(tài)綁定在運(yùn)行時期,綁定的一定是虛函數(shù)的調(diào)用 指令:編譯的是call寄存器 運(yùn)行時才知道
覆蓋:基類和派生類的方法,返回值、函數(shù)名以及參數(shù)列表都相同,而且基類的方法是虛函數(shù),那么派生類的方法就是自動處理成虛函數(shù),他們之間成為覆蓋關(guān)系。
在類內(nèi)部添加一個虛擬函數(shù)表指針,該指針指向一個虛擬函數(shù)表,該虛擬函數(shù)表包含了所有的虛擬函數(shù)的入口地址,每個類的虛擬函數(shù)表都不一樣,在運(yùn)行階段可以循環(huán)脈絡(luò)找到自己的函數(shù)入口。純虛函數(shù)相當(dāng)于占位符,現(xiàn)在虛函數(shù)占一個位置由派生類實(shí)現(xiàn)后再把真正的函數(shù)指針填進(jìn)去。
5、虛析構(gòu)函數(shù)
- 哪些函數(shù)不能實(shí)現(xiàn)成虛函數(shù)?
虛函數(shù)依賴:
- 虛函數(shù)能產(chǎn)生地址,存儲在vfptr當(dāng)中
- 對象必須存在(vfptr -> vftable -> 虛函數(shù)地址)
構(gòu)造函數(shù):沒有虛構(gòu)造函數(shù)?。?!
- virtual+構(gòu)造函數(shù) NO!
- 構(gòu)造函數(shù)中(調(diào)用的任何函數(shù),都是靜態(tài)綁定的)調(diào)用虛函數(shù),也不會發(fā)生靜態(tài)綁定
派生類對象構(gòu)造過程:先調(diào)用的是基類的構(gòu)造函數(shù),才調(diào)用派生類的構(gòu)造函數(shù)。
static靜態(tài)成員方法 NO!
- 虛析構(gòu)函數(shù) 析構(gòu)函數(shù)調(diào)用的時候,對象是存在的!
- 什么時候把基類的析構(gòu)函數(shù)必須實(shí)現(xiàn)成虛函數(shù)?
基類的指針(引用)指向堆上new出來的派生來對象的時候,delete pb(基類指針),它調(diào)用析構(gòu)函數(shù)的時候,必須發(fā)生動態(tài)綁定,否則會導(dǎo)致派生類的析構(gòu)函數(shù)無法調(diào)用
- 虛函數(shù)和動態(tài)綁定
問題:是不是虛函數(shù)的調(diào)用一定就是動態(tài)綁定?肯定不是!
在類的構(gòu)造函數(shù)當(dāng)中,調(diào)用虛函數(shù),也是靜態(tài)綁定(構(gòu)造函數(shù)中調(diào)用其他函數(shù)(虛),不會發(fā)生動態(tài)綁定)
靜態(tài)綁定 用對象本身調(diào)用虛函數(shù),是靜態(tài)綁定
動態(tài)綁定:
- 必須由指針調(diào)用虛函數(shù)
- 必須由引用變量調(diào)用虛函數(shù)
- 虛函數(shù)通過指針或者引用的調(diào)用,才發(fā)生動態(tài)綁定
6、如何解釋多態(tài)
-
靜態(tài)(編譯時期)的多態(tài):函數(shù)重載、模板(函數(shù)模板和類模板)
bool compare(int , int) { } bool cpmpare(double, double) { } compare(10,20); call compare_int_int 在編譯階段就確定好調(diào)用的函數(shù)版本 compare(10.5, 20.5); call compare_double_double 在編譯階段就確定好調(diào)用的函數(shù)版本 template<typename T> bool compare(T a, T b) { } compare<int>(10,20); => int 實(shí)例化一個compare<int> compare(10.5 ,20.5); => double 實(shí)例化一個 compare<double>
-
動態(tài)(運(yùn)行時期)的多態(tài):
在繼承結(jié)構(gòu)中,基類指針(引用)指向派生類對象,通過指針(引用)調(diào)用同名覆蓋方法(虛函數(shù)),基類指針指向哪個派生類對象,就會調(diào)用哪個派生類對象的同名覆蓋方法,稱為多態(tài)。
pbase->show();
多態(tài)底層是通過動態(tài)綁定來實(shí)現(xiàn)的,pbase->訪問誰的vfptr ->繼續(xù)訪問誰的vftable -> 當(dāng)然調(diào)用的是對應(yīng)的派生類對象的方法了。
7、繼承
廣義的繼承有三種實(shí)現(xiàn)形式:
- 實(shí)現(xiàn)繼承:指使用基類的屬性和方法而無需額外編碼的能力。
- 可視繼承:子窗口使用父窗口的外觀和實(shí)現(xiàn)代碼。
- 接口繼承:僅使用屬性和方法,實(shí)現(xiàn)滯后到子類
好處:
- 可以做代碼的復(fù)用
- 在基類中給所有派生類提供統(tǒng)一的虛函數(shù)接口,讓派生類重寫,然后就可以使用多態(tài)了。
8、抽象類和普通類的區(qū)別
一般把什么類設(shè)計成抽象類?基類
//動物的基類 泛指 類 -> 抽象一個實(shí)體的類型
定義Animal的初衷,并不是讓Animal抽象某個實(shí)體的類型
- string _name; 讓所有的動物實(shí)體類通過繼承Animal直接復(fù)用該屬性
- 給所有的派生類保留統(tǒng)一的覆蓋/重寫接口
擁有純虛函數(shù)的類,叫抽象類?。ˋnimal)
Animal a; NO?。?!
抽象類不能再實(shí)例化對象了,但是可以定義指針和引用變量。
class Animal
{
public:
Animal(string name) : _name(name) { }
virtual void bark() = 0; //純虛函數(shù)
protected:
string _name;
};
//以下是動物實(shí)體類
class Cat : public Animal
{
public:
Cat(string name) : Animal(name) { }
void bark() { cout << _name << "bark: miao miao!" << endl; }
};
class Dog :public Animal
{
public:
Dog(string name):Animal(name) { }
void bark() { cout << _name << "bark: wang wang!" << endl; }
};
class Pig :public Animal
{
Pig(string name) :Animal(name) { }
void bark() { cout << _name << "bark: heng heng! " << endl; }
};
void bark(Animal* p)
{
p->bark(); //Animal::bark虛函數(shù),動態(tài)綁定了
}
int main()
{
Cat cat("貓咪");
Dog dog("二哈");
Pig pig("佩奇");
bark(&cat);
bark(&dog);
bark(&pig);
return 0;
}
9、抽象類(有純虛函數(shù)的類) / 虛基類
virtual
- 修飾成員方法的虛函數(shù)
- 可以修飾繼承方式,是虛繼承。被虛繼承的類,稱作虛基類。
class A
{
public:
virtual void func() { cout << "call A::func" << endl; }
void operator delete(void* ptr)
{
cout << "operator delete p:" << ptr << endl;
free(ptr);
}
private:
int ma;
};
class B :virtual public A
{
public:
void func() { cout << "call B::func" << endl; }
void* operator new(size_t size)
{
void* p = malloc(size);
cout << "operator new p:" << p << endl;
return p;
}
private:
int mb;
};
A a; 4個字節(jié)
B b; ma,mb 8個字節(jié)
int main()
{
B b;
A* p = &b;
cout << "main p:" << p << endl;
p->func();
return 0;
}
基類指針指向派生類對象,永遠(yuǎn)指向的是派生類基類部分?jǐn)?shù)據(jù)的起始地址。
10、C++多繼承
菱形繼承的問題:派生類有多份間接基類的數(shù)據(jù), 設(shè)計的問題
使用虛繼承
好處 :可以做更多代碼的復(fù)用。
C++語言級別提供的四種類型轉(zhuǎn)換方式:
- const_cast:去掉常量屬性的一個類型轉(zhuǎn)換。
- static_cast:提供編譯器認(rèn)為安全的類型轉(zhuǎn)換(沒有任何聯(lián)系的類型之間的轉(zhuǎn)換就被否定)。
- reinterpret_cast:類似于C風(fēng)格的強(qiáng)制類型轉(zhuǎn)換。
- dynamic_cast:主要用于在繼承結(jié)構(gòu)中,可以支持RTTI類型識別的上下轉(zhuǎn)換。
11、函數(shù)對象
把有operator() 小括號運(yùn)算符重載函數(shù)的對象,稱作函數(shù)對象或者仿函數(shù)。
- 通過函數(shù)對象調(diào)用operator(),可以省略函數(shù)的調(diào)用開銷,比通過函數(shù)指針調(diào)用函數(shù)(不能夠inline內(nèi)聯(lián)調(diào)用)效率高。
- 因?yàn)楹瘮?shù)對象是用類生成的,所以可以添加相關(guān)的成員變量,用來記錄函數(shù)對象使用時的信息。
//函數(shù)對象
template
12、菱形繼承
多重繼承-菱形繼承的問題:
- 好處:可以做更多代碼的復(fù)用。
基類被多個派生類用就需要是虛繼承,不然就會報錯。
基類需要被最后的派生類初始化。
-
C++
+關(guān)注
關(guān)注
22文章
2114瀏覽量
73882 -
STL
+關(guān)注
關(guān)注
0文章
86瀏覽量
18392 -
面向?qū)ο?/span>
+關(guān)注
關(guān)注
0文章
64瀏覽量
10004
發(fā)布評論請先 登錄
相關(guān)推薦
Labview 之面向對象編程。 里面有個例子 和視頻教程地址
C#入門教程之面向對象編程簡介的詳細(xì)資料概述
![<b class='flag-5'>C</b>#入門教程<b class='flag-5'>之面向</b><b class='flag-5'>對象</b>編程簡介的詳細(xì)資料概述](https://file.elecfans.com/web1/M00/7C/D1/o4YBAFwHbFSALNfWAAD9Ojo-zps851.png)
Visual C++教程之C++的基礎(chǔ)知識介紹
![Visual <b class='flag-5'>C++</b>教程之<b class='flag-5'>C++</b>的<b class='flag-5'>基礎(chǔ)知識</b>介紹](https://file.elecfans.com/web1/M00/84/E7/o4YBAFxmeHWAZNqUAADfu76tYr0942.png)
C++語言和面向對象程序設(shè)計教程
C++基礎(chǔ)知識之面向對象篇2
PyTorch教程3.2之面向對象的設(shè)計實(shí)現(xiàn)
![PyTorch教程3.2<b class='flag-5'>之面向</b><b class='flag-5'>對象</b>的設(shè)計實(shí)現(xiàn)](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評論