1.函數(shù)調(diào)用的壓棧過程
入棧過程:函數(shù)參數(shù)從右向左入棧翻斟,函數(shù)運行狀態(tài)入棧主慰,函數(shù)返回地址入棧移宅,代碼區(qū)跳轉(zhuǎn)(從當(dāng)前代碼區(qū)跳轉(zhuǎn)到調(diào)用的函數(shù)入口)
出棧過程:保存返回值,彈出返回地址泡徙,返回值橱鹏,彈出參數(shù),跳轉(zhuǎn)到原始地址
https://blog.csdn.net/u011555996/article/details/70211315
2.創(chuàng)建只有在堆/棧上生成的類
只能在堆上生成對象的類:
方案1:構(gòu)造函數(shù)私有化
- 不能讓這個類在棧上創(chuàng)建, 由于在棧上創(chuàng)建對象要直接調(diào)用構(gòu)造函數(shù), 如果我們把構(gòu)造函數(shù)私有化, 就 無法在棧上創(chuàng)建對象了
- 那么我們又如何在堆上創(chuàng)建對象呢, 由于創(chuàng)建對象必定要調(diào)用構(gòu)造函數(shù), 在我們不定義其他構(gòu)造函數(shù)時, 我們已經(jīng)將兩個默認構(gòu)造函數(shù)已經(jīng)私有, 在類外肯定是調(diào)用不到構(gòu)造函數(shù), 我們只有定義一個公有的靜態(tài)成員函數(shù) ,在其內(nèi)部用new在堆區(qū)創(chuàng)建對象并返回其指針, (這里有很難以理解的一點, 在靜態(tài)成員函數(shù)中用new 創(chuàng)建一個對象時, 也會調(diào)用構(gòu)造函數(shù), 我們知道, 靜態(tài)成員函數(shù)不能調(diào)用成員函數(shù), 那么new是如何調(diào)到構(gòu)造函數(shù)的呢? 這得從靜態(tài)成員函數(shù)為什么不能訪問成員函數(shù)說起, 每一個對象都有一個隱含的this指針, 訪問成員函數(shù)實際上時通過this指針調(diào)用的, 而在構(gòu)造函數(shù)調(diào)用前還沒有實例化出對象, 也就沒有this指針, 所以構(gòu)造函數(shù)不需要this指針調(diào)用, 靜態(tài)成員函數(shù)也就可以調(diào)用構(gòu)造函數(shù)了), 這點解釋通了
#pragma once
class T1 {
T1(int val):b(val) {
}
T1(T1& x):b(x.b){
}
public:
int b;
static T1* newT1_p(int val = 0) {
return new T1(val);
}
static T1& newT1(int val = 0) {
return *new T1(val);
}
};
方案2:析構(gòu)函數(shù)私有化
將析構(gòu)函數(shù)私有化, 在棧上也就不能直接創(chuàng)建對象了, 因為編譯器在編譯時會進行檢測, 那沒有析構(gòu)函數(shù)也是不行的, 我們還需要實現(xiàn)一個函數(shù)來調(diào)用私有的析構(gòu)函數(shù), (這個思路就比構(gòu)造函數(shù)私有好理解多了)
class T2 {
~T2() {
delete this;
}
public:
int b;
T2(int val = 0) :b(val) {
}
void Destroy() {
this->~T2();
}
};
只能在棧上生成的類
由于使用new進行申請會在堆上生成新類,因此將operator new 重載為私有函數(shù)即可莉兰。
class T3 {
void* operator new(size_t val) {}
public:
int a;
T3(int val = 0) :a(val) {
}
};
#include<iostream>
using namespace std;
int main() {
T3 a;
cout << a.a << endl;
T3 b(10);
cout << b.a << endl;
system("pause");
return 0;
}
https://blog.csdn.net/qq_41071068/article/details/102696312
3.排序算法時間復(fù)雜度
4.面向?qū)ο蟮娜筇匦蕴粑В宕鬁蕜t
三大特性:
封裝:將具體實現(xiàn)細節(jié)封裝起來,留下外部接口供調(diào)用糖荒,保證安全性
繼承:子類對父類屬性和方法進行繼承杉辙,減少了在內(nèi)容擴展時的重構(gòu)復(fù)雜度
多態(tài):子類可對父類的屬性和方法進行改寫
五大原則:
單一職責(zé)原則:每一個類只有一個引起其變化的變量
開放閉合原則:類對擴展開放,對修改封閉捶朵,保證在進行功能變更時不改變原有代碼結(jié)構(gòu)
里氏替換原則:所有存在父類的位置均可以使用子類進行替代
依賴倒置原則:高層次模塊不應(yīng)以來低層次模塊蜘矢,兩者都應(yīng)該依賴于抽象。
接口隔離原則:接口應(yīng)小而完備综看,不應(yīng)存在過多無用的屬性和方法
5.x=x+1,x+=1,x++效率
x=x+1:1:讀取右x的地址->2.x+1.做加1操作->3.讀取左x的地址品腹。->4.將右值傳給左邊的x(編譯器并不認為左右x的地址相同)
x+=1:1:讀取x的地址->2.x+1.做加1操作->3.將右值傳給的x
x++:1.讀取x地址->2.x自增
6.static和extern的區(qū)別
static是對變量進行定義,同時對其進行初始化寓搬;extern只是對變量或函數(shù)進行聲明而非定義珍昨,編譯時不會檢查extern聲明的對象,但是鏈接過程中如果未找到則會報錯句喷。
7.strcpy的風(fēng)險
1.如果要拷貝的字符串長度大于原始字符串長度镣典,則會發(fā)生越界
2.拷貝數(shù)組的時候結(jié)尾未出現(xiàn)‘/0’,則會越界復(fù)制
3.當(dāng)拷貝的字符串是自身的一部分的時候可能會出錯
strcpy返回值是拷貝后指針唾琼,增加靈活性兄春。
8.DLL HELL
還未看
https://blog.csdn.net/qwertyupoiuytr/article/details/53999586
9.當(dāng)拷貝構(gòu)造函數(shù)使用值傳遞時會有什么錯誤
使用值傳遞的時候會調(diào)用拷貝構(gòu)造函數(shù)生成一個該類的副本,但是拷貝構(gòu)造函數(shù)使用值傳遞锡溯,會套娃赶舆,所以拷貝構(gòu)造函數(shù)必須使用引用傳遞。
10.virtual和static為什么不能同時作用于一個函數(shù)
1.static成員不屬于任何類的對象祭饭,而是由類公用芜茵,因此加上virtual無意義
2.虛函數(shù)的調(diào)用關(guān)系:this -> vptr -> vtable ->virtual function。由于static函數(shù)沒有this指針倡蝙,因此無法進行虛函數(shù)訪問九串。
11.vptr生成時間:
在基類構(gòu)造函數(shù)之后,自身構(gòu)造函數(shù)或初始化列表之前
構(gòu)造函數(shù)調(diào)用虛函數(shù):
派生類對象構(gòu)造期間進入基類的構(gòu)造函數(shù)時寺鸥,對象類型變成了基類類型猪钮,而不是派生類類型。
同樣胆建,進入基類析構(gòu)函數(shù)時烤低,對象也是基類類型。
12.紅黑樹較AVL樹的優(yōu)點
黑樹的查詢性能略微遜色于AVL樹笆载,因為其比AVL樹會稍微不平衡最多一層扑馁,也就是說紅黑樹的查詢性能只比相同內(nèi)容的AVL樹最多多一次比較涯呻,但是,紅黑樹在插入和刪除上優(yōu)于AVL樹檐蚜,AVL樹每次插入刪除會進行大量的平衡度計算魄懂,而紅黑樹為了維持紅黑性質(zhì)所做的紅黑變換和旋轉(zhuǎn)的開銷,相較于AVL樹為了維持平衡的開銷要小得多
搜索次數(shù)多:AVL闯第;查找次數(shù)多:紅黑樹
13.空類大小為什么是1
保證每個實例都有一個獨特的地址市栗,不會產(chǎn)生重復(fù),編譯器會給空類添加隱藏字節(jié)咳短。
14.迭代器失效的情況及處理方法
順序式容器:在進行刪除操作后填帽,刪除節(jié)點之后的迭代器會失效,在刪除之前將迭代器與初始位置的相對偏移記錄咙好,后續(xù)再做修改
關(guān)聯(lián)式容器/鏈表式容器:只會使刪除節(jié)點的迭代器失效篡腌,刪除前做移動即可
15.同步異步,阻塞非阻塞
同步:主動等消息
異步:等人發(fā)消息
阻塞:等返回消息勾效,返回前掛起
非阻塞:獲得返回消息前先做別的事情
并發(fā):一段時間內(nèi)多個線程同時運行嘹悼,但是同一時刻只能有一個線程
并行:平行線,多個線程同時運行
16.ping的過程
首先通過主機IP/掩碼/目的主機IP檢查是否在同一網(wǎng)段內(nèi)
17.C++內(nèi)存泄漏的幾種情況
1.類的構(gòu)造和析構(gòu)函數(shù)中沒有正確調(diào)用匹配的new和delete運算符
2.沒有正確清除嵌套的對象指針
3.在釋放對象數(shù)組時在delete中沒有使用方括號
4.指向?qū)ο蟮闹羔様?shù)組不等同于對象數(shù)組
5.缺少拷貝構(gòu)造函數(shù)
6.缺少重載賦值運算符
7.返回局部對象的引用或指針(野指針)
8.沒有將基類的析構(gòu)函數(shù)定義為虛函數(shù)
18.int fun() 和 int fun(void)的區(qū)別?
這里考察的是c 中的默認類型機制层宫。
在c中杨伙,int fun() 會解讀為返回值為int(即使前面沒有int,也是如此萌腿,但是在c++中如果沒有返回類型將報錯)限匣,輸入類型和個數(shù)沒有限制, 而int fun(void)則限制輸入類型為一個void毁菱。
在c++下米死,這兩種情況都會解讀為返回int類型,輸入void類型贮庞。
19. 在C中用const 能定義真正意義上的常量嗎峦筒?C++中的const呢?
不能窗慎。c中的const僅僅是從編譯層來限定勘天,不允許對const 變量進行賦值操作,在運行期是無效的捉邢,所以并非是真正的常量(比如通過指針對const變量是可以修改值的),但是c++中是有區(qū)別的商膊,c++在編譯時會把const常量加入符號表伏伐,以后(仍然在編譯期)遇到這個變量會從符號表中查找,所以在C++中是不可能修改到const變量的晕拆。
補充:
1). c中的局部const常量存儲在椕牯幔空間材蹬,全局const常量存在只讀存儲區(qū),所以全局const常量也是無法修改的吝镣,它是一個只讀變量堤器。
2). 這里需要說明的是,常量并非僅僅是不可修改末贾,而是相對于變量闸溃,它的值在編譯期已經(jīng)決定,而不是在運行時決定拱撵。
3).c++中的const 和宏定義是有區(qū)別的辉川,宏是在預(yù)編譯期直接進行文本替換,而const發(fā)生在編譯期拴测,是可以進行類型檢查和作用域檢查的乓旗。
4).c語言中只有enum可以實現(xiàn)真正的常量。
5). c++中只有用字面量初始化的const常量會被加入符號表集索,而變量初始化的const常量依然只是只讀變量屿愚。
6). c++中const成員為只讀變量,可以通過指針修改const成員的值务荆,另外const成員變量只能在初始化列表中進行初始化妆距。
下面我們通過代碼來看看區(qū)別。
同樣一段代碼蛹含,在c編譯器下毅厚,打印結(jié)果為*pa = 4, 4
在c++編譯下打印的結(jié)果為 *pa = 4浦箱, 8
int main(void)
{
const int a = 8;
int *pa = (int *)&a;
*pa = 4;
printf("*pa = %d, a = %d", *pa, a);
return 0;
}
20.模板函數(shù)和模板類的特例化
引入的原因:編寫單一的模板吸耿,它能適應(yīng)大眾化,使每種類型都具有相同的功能酷窥,但對于某種特定類型咽安,如果要實現(xiàn)其特有的功能,單一模板就無法做到蓬推,這時就需要模板特例化妆棒。
定義:是對單一模板提供的一個特殊實例,它將一個或多個模板參數(shù)綁定到特定的類型或值上沸伏。
函數(shù)模板特例化:必須為原函數(shù)模板的每個模板參數(shù)都提供實參糕珊,且使用關(guān)鍵字template后跟一個空尖括號對<>,表明將原模板的所有模板參數(shù)提供實參。
template<typename T> //函數(shù)模板
int compare(const T &v1,const T &v2)
{
if(v1 > v2) return -1;
if(v2 > v1) return 1;
return 0;
}
//模板特例化,滿足針對字符串特定的比較毅糟,要提供所有實參红选,這里只有一個T
template<>
int compare(const char const &v1,const char const &v2)
{
return strcmp(p1,p2);
}
模板類特例化:原理類似函數(shù)模板,不過在類中姆另,我們可以對模板進行特例化喇肋,也可以對類進行部分特例化坟乾。對類進行特例化時,仍然用template<>表示是一個特例化版本蝶防,例如:
template<typename T>class Foo
{
void Bar();
void Barst(T a)();
};
template<>
void Foo<int>::Bar()
{
//進行int類型的特例化處理
}
21.shared_ptr實現(xiàn)
#include <memory>
#include <iostream>
using namespace std;
template<typename T>
class smart{
private:
T* _ptr;
int* _count; //reference counting
public:
//構(gòu)造函數(shù)
smart(T* ptr = nullptr):_ptr(ptr){
if (_ptr){
_count = new int(1);
}
else{
_count = new int(0);
}
}
//拷貝構(gòu)造
smart(const smart& ptr){
if (this != &ptr){
this->_ptr = ptr._ptr;
this->_count = ptr._count;
(*this->_count)++;
}
}
//重載operator=
smart operator=(const smart& ptr){
if (this->_ptr == ptr._ptr){
return *this;
}
if (this->_ptr){
(*this->_count)--;
if (this->_count == 0){
delete this->_ptr;
delete this->_count;
}
}
this->_ptr = ptr._ptr;
this->_count = ptr._count;
(*this->_count)++;
return *this;
}
//operator*重載
T& operator*(){
if (this->_ptr){
return *(this->_ptr);
}
}
//operator->重載
T& operator->(){
if (this->_ptr){
return this->_ptr;
}
}
//析構(gòu)函數(shù)
~smart(){
(*this->_count)--;
if (*this->_count == 0){
delete this->_ptr;
delete this->_count;
}
}
//return reference counting
int use_count(){
return *this->_count;
}
};
int main(){
smart<int> sm(new int(10));
cout << "operator*():" << *sm << endl;
cout << "reference counting:(sm)" << sm.use_count() << endl;
smart<int> sm2(sm);
cout <<"copy ctor reference counting:(sm)"<< sm.use_count() << endl;
smart<int> sm3;
sm3 = sm;
cout <<"copy operator= reference counting:(sm)"<< sm.use_count() << endl;
cout << &sm << endl;
return 0;
}
22.虛函數(shù)使用模板函數(shù)會發(fā)生什么
使用模板函數(shù)時甚侣,程序在運行時會動態(tài)對模板進行參數(shù)類型匹配;而存在虛函數(shù)的類在編譯時需要生成虛函數(shù)表间学,此時需要獲得固定的虛函數(shù)類型殷费,存在模板虛函數(shù)時編譯器需要對全部程序進行遍歷來確定一共存在多少實例化的虛函數(shù),效率非常低下菱鸥。