由於面試的時候被問到過幾次智能指針,所以閒下來打算從頭理一遍智能指針的實現思路,自己實現一個shared_ptr,也能夠更好地理解智能指針。
智能指針說白了就是用類把指針封裝起來,這樣就不必擔心內存泄漏的問題了,因爲類的對象在出作用域的時候會自動調用析構函數。
因此智能指針要具備指針的基本功能:
- 首先要有一個T類型的指針,用於封裝實際的指針(初始化);
- 其次一定要有一個int型指針,用於引用計數,同步更新指向同一內存空間的智能指針有多少個;
- 要自己寫構造函數、拷貝構造函數、拷貝賦值函數及析構函數;
- 重載 解引用運算符 * 和 指向運算符 -> ;
- 最後useCount函數用於獲取該對象所封裝指針的引用計數。
另外還編寫了幾個測試樣例,用於驗證是否實現了shared_ptr的基本功能,具體代碼如下:
#include <iostream>
using namespace std;
template<typename T>
class SharedPtr {
private:
T* m_ele;
int* m_num;
public:
SharedPtr(): m_ele(nullptr){
cout << "Hello Constructor 1 !" << endl;
m_num = new int(0);
}
SharedPtr(T* x): m_ele(x){
cout << "Hello Constructor 2 !" << endl;
m_num = new int(1);
}
SharedPtr(const SharedPtr& x){
cout << "Hello Copy Constructor !" << endl;
m_ele = x.m_ele;
m_num = x.m_num;
(*m_num)++;
}
/*
關於拷貝賦值函數,這裏返回值加引用和不加引用區別很大:
如果返回值是引用對象的話,拷貝賦值時只會調用該賦值函數;
如果返回值是值對象,除了要調用該賦值函數,還要在該賦值函數結束時調用copy構造函數
創建一個臨時對象作爲返回值,返回後賦給等號左邊的對象,
這個臨時對象完成其使命後,在該賦值語句結束時,會調用析構函數,釋放該臨時對象
因此不加引用會比加引用多調用一次拷貝賦值和析構函數,這點一定要明確!
*/
SharedPtr& operator=(const SharedPtr& x){
cout << "Copy Assignment !" << endl;
(*m_num)--;
if((*m_num) == 0){
delete m_ele;
delete m_num;
}
m_ele = x.m_ele;
m_num = x.m_num;
(*m_num)++;
return *this; //不是構造函數,要有返回值!
}
T& operator*(){
return *(this->m_ele);
}
T* operator->(){
return this->m_ele;
}
~SharedPtr(){
cout << "Hello Destructor ! I'm " << *m_ele << endl;
//調用析構函數,則其引用計數減1,若引用計數減爲0,則將其封裝的指針和計數指針都釋放掉
(*m_num)--;
if((*m_num) == 0){
delete m_ele;
delete m_num;
}
}
int useCount(){
return *(this->m_num);
}
};
int main() {
int* x=new int(520);
SharedPtr<int> test(x);
cout << "test引用計數: " << test.useCount() << " 指向內存的值:" << *test << endl;
int* y=new int(250);
SharedPtr<int> test2(y);
cout << "test2引用計數: " << test2.useCount() << " 指向內存的值:" << *test2 << endl;
SharedPtr<int> test3(test2);
cout << "test2引用計數: " << test2.useCount() << " 指向內存的值:" << *test2 << endl;
cout << "test3引用計數: " << test3.useCount() << " 指向內存的值:" << *test3 << endl;
test3 = test;
cout << "test引用計數: " << test.useCount() << " 指向內存的值:" << *test << endl;
cout << "test2引用計數: " << test2.useCount() << " 指向內存的值:" << *test2 << endl;
cout << "test3引用計數: " << test3.useCount() << " 指向內存的值:" << *test3 << endl;
return 0;
}
最終的輸出結果表明該實現是準確的,結果如下: