使用對象來管理資源,可以避免因個人疏忽帶來的一些低級錯誤,但是不是每件事都是稱心如意的。 一些函數依然使用原始的資源對象,那麼我們就需要為這些函數提供一個介面,讓他們可以獲取到原始對象。 繼續拿13節的智能指針說事,先上代碼: //SFAutoPtr.h #pragma once template ... ...
使用對象來管理資源,可以避免因個人疏忽帶來的一些低級錯誤,但是不是每件事都是稱心如意的。
一些函數依然使用原始的資源對象,那麼我們就需要為這些函數提供一個介面,讓他們可以獲取到原始對象。
繼續拿13節的智能指針說事,先上代碼:
//SFAutoPtr.h
#pragma once
template<typename T>
class SFAutoPtr {
private:
T* pointer; //對象指針
size_t *ref_count; //引用計數
void dec() { //減少引用計數
if(*ref_count == 0) { //如果當前引用計數為0,則應該釋放資源
delete pointer;
pointer = nullptr;
delete ref_count;
ref_count = nullptr;
return;
}
--*ref_count;
}
void inc() { //增加引用計數
++*ref_count;
}
public:
SFAutoPtr() : //預設構造函數,生成一個指針
pointer(new T),
ref_count(new size_t(0)) {}
template<typename ... Init_Type>
SFAutoPtr(Init_Type...args) : //帶參數的構造函數,對象帶有指針
pointer(new T(args...)),
ref_count(new size_t(0)) {}
SFAutoPtr(const SFAutoPtr<T>& other) { //拷貝構造函數,增加引用計數
pointer = other.pointer;
ref_count = other.ref_count;
inc();
}
bool operator==(const SFAutoPtr<T>& other) const{ //等於操作符,判斷指針是否相等,這時候不應該比較ref_count
return pointer == other.pointer;
}
const SFAutoPtr<T>& operator=(const SFAutoPtr<T>& other) { //賦值運算符,需要將當前引用計數-1,賦值的引用計數+1
if(this == &other)
return *this;
dec();
pointer = other.pointer;
ref_count = other.ref_count;
inc();
return *this;
}
T operator*(int) { //解引用運算符
return *pointer;
}
operator T*() { //指針運算符,適用於使用指針作為參數的函數
return pointer;
}
T* operator->() { //成員訪問操作符
return pointer;
}
~SFAutoPtr() { //析構函數,需要將引用計數-1
dec();
}
};
註意其中的:
operator T*() { //指針運算符,適用於使用指針作為參數的函數
return pointer;
}
這裡就是為需要T*類型作為參數的函數提供介面,如下:
#include <iostream>
using namespace std;
void func(int *){
cout<<"hello world"<<endl;
}
int main(){
SFAutoPtr<int> t;
func(t);
}
SFAutoPtr<int>能夠很好地適應int*類型的參數,這為編碼提供了不少方便,但是隨之也帶來了一些安全隱患:
例如,有人可能寫出下麵這樣的代碼(沒錯,就是小明!!!):
#include <iostream>
using namespace std;
int *p;
void setP(){
p=SFAutoPtr<int>(5);
}
int main(){
setP();
cout<<*p<<endl;
}
這裡不會輸出5,而是會輸出一個隨機值,甚至程式崩潰,因為我們使用了對象管理資源,對象在銷毀時,資源也會被釋放。導致這一現象的原因是AFAutoPtr<int>被隱式轉換為了int*,避免這種調用的方法之一就是,不再提供隱式轉換,確保編寫出的每一個轉換都是程式員想要的,使用get()而非operator T*,將原實現中的operator T*操作符替換為get()函數。有人可能要問,如果程式員非得寫出:
#include <iostream>
using namespace std;
int *p;
void setP(){
SFAutoPtr<int> tmp(5);
p=tmp.get();
}
int main(){
setP();
cout<<*p<<endl;
}
這樣的代碼怎麼辦?
對此我只能說:自作孽,不可活~~~
一般使用對象管理資源,都會提供介面訪問原始對象。是使用隱式訪問還是顯式訪問,這主要看實際需求,是為程式員帶來便捷還是給程式員帶來安全,你說了算~~~