嵌入式c++軟體開發第四講筆記

時義龍發表於2021-01-04

c++模板
概述
模板的作用
1.模板是一種引數化的多型工具
2.所謂引數化的多型性,是指將程式所處理的物件的型別引數化,使一段程式程式碼處理不同型別的物件。
3.採用模板程式設計,可以為各種邏輯功能相同而資料型別不同的程式提供一種程式碼共享機制
程式碼複用
函式模板 連結
template<模板形參表>
返回值型別 函式名(模板函式形參表){
函式定義體
}
模板被編譯了兩次;
模板的編譯需要檢視模板的定義;
類别範本
template<型別形參表>
class <類名>
{
類說明體
}
類别範本的使用
成員模板
例項化兩個類,d和i,但是i卻沒法給d賦值。
成員模板的實現–連結
關鍵字-typename
內嵌依賴型別名
內嵌是指定義在類的定義中的
依賴是指依賴於一個模板引數typedef
型別名是指這裡最終要指出的是個型別名
例項
連結
例項化
隱式例項化
函式模板隱式例項化指的是在發生函式呼叫的時候,如果沒有發現相匹配的函式存在,編譯器就會尋找同名函式模板,如果可以成功進行引數型別推演,就對函式模板進行例項化。
類别範本隱式例項化指的是在使用模板類時才將模板例項化,相對於類别範本顯示例項化而言的
顯示例項化
定義:顯示例項化也稱為外部例項化。在不發生函式呼叫的時候將函式模板例項化,或者在不使用類别範本的時候將類别範本例項化稱之為模板顯示例項化。
函式模板的顯示例項化:template [函式返回型別] [函式模板名]<實際型別列表>(函式引數列表)例如:template void func(const int&);
類别範本的顯示例項化:template class [類别範本名]<實際型別列表>
例如:template class theclass;
作用:減少隱式例項化例項多個函式和類的開銷;最優化例項一次(不同編譯器處理機制可能不同)
using給模板起別名
template
using str_map_t = std::map<std::string, Val>;
// …
str_map_t map1;

模板全特化與偏特化(C++11)
類别範本的全特化
特化模板成員函式而不是模板
//模板泛化
template <typename T1, typename T2>
class A{
T1 data1;
T2 da ta2;
};
//模板全特化
template <>
class A<int, double>{
int data1;
double data2;
};

類别範本的偏特化
//泛型化
template <typename T1,typename T2,typename T3>
class B
{
public:
void myfunc(T2 b)
{
cout << “泛化函式” << endl;
}
};
//偏特化:引數數量
template
class B<int, T2, int>
{
public:
void myfunc(T2 b)
{
cout << “偏特化函式” << endl;
}
};

引數範圍變化:int–const int;T–T*; T–T&
//模板偏特化引數範圍int–const int;T–T*; T–T&
template
class A {
public:
void myfunc()
{
cout << “const T偏特化函式” << endl;
}
};

函式模板全特化
注:函式模板不能偏特化
注:呼叫優先順序:普通函式、過載函式、函式全特化
模板超程式設計
框架、庫
Qt內部開發機制:模板元

c++記憶體管理
C語言記憶體管理
Linux虛擬記憶體空間分佈

記憶體洩露——C語言沒有更好的方法杜絕malloc的導致的記憶體洩漏
C++記憶體管理(1)
如何避免記憶體洩漏?
new/delete的使用
new/delete必須成對出現,new[]/delete []必須成對出現
人為的控制new/delete問題,無法杜絕記憶體洩漏
C++為什麼沒有提供GC機制?
1.沒有共同基類:C++是從C發展而成,允許直接操作指標,允許將一個型別轉換為另一個型別,對於一個指標無法知道它真正指向的型別;而Java或C#都有一個共同基類
2.系統開銷:垃圾回收所帶來的系統開銷,違反了C++的設計哲學,“不為不必要的功能支付代價”,不符合C++高效的特性,使得不適合做底層工作
3.消耗記憶體:C++產生的年代記憶體很少,垃圾回收機制需要佔用更多的記憶體
4.替代方法:C++有解構函式、智慧指標、引用計數去管理資源的釋放,對GC的需求不迫切
智慧指標
作用:
幫助開發者對動態分配物件(new)的生命週期進行管理。能夠有效防止記憶體洩漏
分類:
unique_ptr(C++11)–獨佔指標
shared_ptr(C++11)–共享指標
weak_ptr(C++11)–弱指標
shared_ptr
共享式指標:
多個指標可以同時指向同一個物件(共享所有權,協同工作),當最後一個指標被銷燬或者指向其他物件時,這個物件會被釋放;
工作原理:引用計數增加/減少(原子操作)
引用計數增加
用一個智慧指標初始化另一個智慧指標
函式傳參:傳遞一個智慧指標;
函式返回值:返回一個智慧指標
引用計數減少
給智慧指標賦予新值,指向一個新物件
區域性的智慧指標離開其作用域
定義:
shared_ptr<指向的型別> 智慧指標變數名
shared_ptr是explicit,不可以進行隱式型別轉換,只能直接初始化;
std::make_shared函式
auto p = std::make_shared(“hello world”);
功能:在 堆記憶體中可以動態分配物件,並返回一個shared_ptr;
推薦方法,因為此方法安全,高效;
shared_ptr常規操作
use_count();
功能:返回有多少個shared_ptr智慧指標指向某物件;(引用計數的個數)
unique();
是否該智慧指標獨佔某個物件,獨佔返回true,否則返回false;
reset();
reset()無參使用:若該智慧指標是獨佔某個物件,則釋放該物件,並將智慧指標置nullptr;若不獨佔,引用計數減1,並將該指標置nullptr;
reset()代參使用:若該智慧指標是獨佔某個物件,則釋放該物件,並將該指標指向新物件;若不獨佔,則將該指標指向的物件引用計數減1,並將該指標指向新物件;
例項:p.reset();p.reset(new int());
*解應用
獲得智慧指標指向的物件,並對其操作;
get();
考慮到有些函式引數是裸指標並不是智慧指標,所以需要將智慧指標轉化為裸指標;
獲得智慧指標中儲存的指標(裸指標);
注:獲得裸指標要小心使用,因為智慧指標一旦釋放,裸指標也就失效;
= nullptr;
將該智慧指標引用計數減1,並置nullptr,若引用計數為零,將指向物件釋放;
指定刪除器
why:有些情況,預設刪除器處理不了(shared_ptr管理動態陣列),需要我們自己指定刪除器;
例項–連結
移動語義:std::move();
weak_ptr
概述
1.weak_ptr弱指標,不會控制物件的生命週期(不會改變物件的引用計數),shared_ptr釋放指向物件時,是不會考慮weak_ptr是否指向該物件
2.weak_ptr不是獨立指標,不能單獨操作所指向的資源;
作用
1.weak_ptr指標一般用來輔助shared_ptr的使用(監視shared_ptr指向物件的生命週期)
2.weak_ptr和shared_ptr之間可以相互轉換,shared_ptr可以直接賦值給weak_pt,但是反過來是行不通的,需要使用lock函式。
weak_ptr的常規操作
lock();
呼叫lock函式來獲得shared_ptr(如果物件已經被釋放,則返回一個空的shared_ptr)
use_count();
功能:返回有多少個shared_ptr智慧指標指向某物件;(引用計數的個數)
expired();
判斷弱指標是否過期(所檢測的物件是否被釋放true/false)
reset();
將該弱指標設定為空,弱引用計數減1,強引用計數不變
shared_ptr & weak_ptr
尺寸
shared_ptr和weak_ptr一樣大,是裸指標的兩倍;
常見使用問題
shared_ptr多次引用同一資料,會導致兩次釋放同一記憶體
使用shared_ptr包裝this指標導致區域性物件會釋放兩次-解決辦法: enable_shared_from_this模板類
shared_ptr迴圈引用導致記憶體洩露
unique_ptr
獨佔式指標(專屬所有權)
同一時刻,只能有一個unique_ptr指向這個物件;當指標銷燬,指向的物件也銷燬;
初始化
手動初始化:unique_ptr p;或unique p(new int(5))
std::make_unique函式(C++14)
unique_ptr常規操作
不支援操作:該指標不支援拷貝和賦值操作;
移動語義std::move();
release();
放棄對指標的控制權,將該指標置nullptr,返回裸指標;
reset();
reset()無參使用:若該智慧指標是獨佔某個物件,則釋放該物件,並將智慧指標置nullptr;
reset()代參使用:若該智慧指標是獨佔某個物件,則釋放該物件,並將該指標指向新物件;
例項:p.reset();p.reset(new int());
*解應用
獲得智慧指標指向的物件,並對其操作;
get();
獲得智慧指標中儲存的指標(裸指標);
= nullptr;
將該智慧指標引用計數減1,並置nullptr,若引用計數為零,將指向物件釋放;
指定刪除器
語法:unique_ptr<指向物件型別,刪除器的型別> 智慧指標變數名
自定義刪除函式
void mydeleter(string *s)
{
delete s;
}

using MYFUNC = void (*)(string *);// tyepdef void (*MYFUNC)(string *);

unique_ptr<string,MYFUNC> p(new string(“hello world”),mydeleter);

lambda函式—decltype()
auto mydeleter = [](string *s)
{
delete s;
};

unique_ptr<string,decltype(mydeleter)> p(new string(“hello world”),mydeleter);

指定不同的刪除器會導致不同的unique_ptr;
unique_ptr尺寸:
和裸指標一樣大,但是指定自定義刪除函式會影響尺寸大小;
C++記憶體管理(2)
記憶體池
起因:malloc:記憶體浪費,頻繁分配小塊記憶體,則浪費更加顯得明顯
作用:減少malloc的次數,減少malloc()呼叫次數就意味著減少對記憶體的浪費
原理:用malloc申請一大塊記憶體,當要分配的時候,從這一大塊記憶體中一點一點的分配,當一大塊記憶體分配的差不多的時候,再用malloc再申請一大塊記憶體,然後再一點一點的分配給你
記憶體池的實現v1.0–問題:next指標佔用4個位元組,每塊記憶體都存在next指標,導致空間的浪費–連結
記憶體池的實現v2.0
嵌入式指標–連結
使用嵌入式指標改進記憶體池–連結

總結
C++記憶體管理的特點(智慧指標)
智慧指標的種類及使用
區分new operator(全域性new)、operator new
過載各類版本的operator new
placement new的作用理解
記憶體池的實現

相關文章