C++實現智慧指標(一)
阿新 • • 發佈:2019-01-23
參考自《C5-C++ Primer》和實驗樓相關實驗。
一. 智慧指標概念
- 智慧指標的引入:
C++中,通過new/delete這對運算子進行動態記憶體的管理。動態記憶體使用很容易出問題,因為確保在正確時間釋放記憶體是很困難的。有時忘記釋放記憶體,產生記憶體洩漏;有時在指標還引用記憶體時就釋放了它,產生引用非法記憶體的指標。因此,為了更容易和安全地使用動態記憶體,C++11提供了智慧指標(smart pointer)型別來管理動態物件。
- 智慧指標的使用:
標準提供兩種智慧指標:shared_ptr允許多個指標指向同一個物件;unique_ptr獨佔所指物件;weak_ptr指向shared_ptr所管理的物件。本實驗主要實現shared_ptr的功能。
- 智慧指標的實現原理:
1. 解構函式,物件被銷燬時會被呼叫的一個函式,對於基於棧的物件而言,如果物件離開其作用域則物件會被自動銷燬,而此時解構函式也自動會被呼叫。
2. 引用計數技術,維護一個計數器用於追蹤資源(如記憶體)的被引用數,當資源被引用時,計數器值加1,當資源被解引用時,計算器值減1。
智慧指標的大致實現原理就是在解構函式中,檢查所引用物件的引用計數,如果引用計數為0,則真正釋放該物件記憶體。
二. 實現版本v1
- 定義智慧指標類
- 建立建構函式:具備預設建構函式;指定型別建構函式
- 定義為模板類
- 解構函式:釋放記憶體
/* * file name : smartpointer.h * desp : 智慧指標版本v1 */ #ifndef __SMARTPOINTER_H__ #define __SMARTPOINTER_H__ template <typename T> // 將智慧指標類定義成模板類 class SmartPointer { public: // 預設建構函式 SmartPointer():mPointer(NULL) {std::cout <<"create unknown smart pointer."<< std::endl;} // 接收不同指標型別的建構函式 SmartPointer(T *p):mPointer(p) {std::cout <<"create smart pointer at "<<static_cast<const void*>(p)<<std::endl;} // 解構函式 ~SmartPointer(){ std::cout << "release smart pointer at "<<static_cast<const void*>(mPointer)<<std::endl; // 實現記憶體資源自動銷燬機制 if (mPointer) delete mPointer; } private: T *mPointer; // 指向智慧指標實際對應的記憶體資源,根據引數自動推導規則,定義內部資源指標型別 }; #endif // __SMARTPOINTER_H__
測試智慧指標:測試檔案 sptestcase1.cpp
/*
* file name : sptestcase1.cpp
* desp : 智慧指標測試程式碼 case1 測試智慧指標的建立與銷燬
*/
#include <iostream>
#include "smartpointer.h"
class SomeClass{
public:
SomeClass(){std::cout << "SomeClass default constructor !"<<std::endl;}
};
void testcase1()
{
// 建立一個不知所指的智慧指標
SmartPointer<char> spunknown;
// 建立空智慧指標
SmartPointer<char> spnull = NULL;
// 建立指向具體類的智慧指標
SmartPointer<SomeClass> spclass = new SomeClass;
// 建立字串的智慧指標
SmartPointer<const char> spstr = "Hello world!";
}
int main(void)
{
testcase1();
return 0;
}
編譯執行:
$ g++ -o sptestcase1 sptestcase1.cpp
$ ./sptestcase1
執行結果分析:
- V1版本不足
簡單實現智慧指標類,智慧指標釋放指向“hello world”指標時出錯。我們的智慧指標不能指向無法被delete釋放的記憶體資源。
三. 知識點查漏補缺
- new的不同用法
(1)int *p=new int;//無初始化
(2)int *p=new int(5);//初始化為5
(3)建立類 Test *test=new Test(); delete test;//加括號呼叫沒有引數的建構函式,不加括號呼叫預設建構函式或唯一的建構函式
(4)int *p=new int[10];//開闢容量為10的陣列 delete []p;
- 類模板
//類的定義
template<class T>
class Test
{
private:
T n;
const T i;
public:
Test():i(0) {}
Test(T k);
~Test(){}
void print();
T operator+(T x);
};
//類的使用
類名<實際的型別> eg.Test<int> test;//宣告一個物件
- staic_cast<const void *>(p)
//地址?轉換型別?
我們這裡定義的是模版類,因為不知道這個指標的具體型別。使用使用了const void *。
const void * p 這是定義了一個指標p,p可以指向任意型別的值,但它指向的值必須是常量。在這種情況下,我們不能修改被指向的物件,但可以使指標指向其他物件。