版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 by-sa 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明徒溪。
本文鏈接:http://www.reibang.com/p/b8d6b10da667
智能指針
1.什么是智能指針
最近有個(gè)段子
- C語言:搬石頭砸自己的腳梳侨;
- C++:搬石頭砸自己的腳,也可能砸別人的腳材蛛;
- python:點(diǎn)個(gè)按鈕圆到,自動(dòng)搬石頭;
在三大常用語言中卑吭,C/C++, java芽淡,python中,通常情況下C/C++性能最好豆赏,但是大部分開發(fā)這都喜歡java和python挣菲,其中主要的原因之一是C/C++缺少智能內(nèi)存回收,在復(fù)雜的系統(tǒng)中掷邦,經(jīng)常遇到一個(gè)常見的問題 -- 內(nèi)存泄露白胀。
對(duì)于一個(gè)程序員,碼農(nóng)最頭大的事情就是內(nèi)存泄露抚岗。君不見或杠,內(nèi)存泄露吼三吼。
C++的開發(fā)這都會(huì)想苟跪,有沒有一種方式能像java和python一樣方便廷痘,系統(tǒng)自動(dòng)釋放呢?
unique_ptr 是C++ 11 提供的用于防止內(nèi)存泄漏的智能指針中的一種實(shí)現(xiàn),獨(dú)享被管理對(duì)象指針?biāo)袡?quán)的智能指針(shared_ptr 下次在分享)件已。
2.智能指針的實(shí)現(xiàn)
unique_ptr定義<memory>頭文件中
template <class T, class D = default_delete<T>> class unique_ptr;
template <class T, class D> class unique_ptr<T[],D>;
std::unique_ptr
是通過指針占有并管理另一對(duì)象笋额,并在 unique_ptr
離開作用域時(shí)釋放該對(duì)象的智能指針。
在下列兩者之一發(fā)生時(shí)用關(guān)聯(lián)的刪除器釋放對(duì)象:
通過調(diào)用 get_deleter()(ptr) 兄猩,用潛在為用戶提供的刪除器釋放對(duì)象。默認(rèn)刪除器用 delete 運(yùn)算符鉴未,它銷毀對(duì)象并解分配內(nèi)存枢冤。
unique_ptr
亦可以不占有對(duì)象,該情況下稱它為空 (empty)铜秆。
std::unique_ptr
有兩個(gè)版本:
管理個(gè)對(duì)象(例如以 new 分配)
管理動(dòng)態(tài)分配的對(duì)象數(shù)組(例如以 new[] 分配)
類滿足可移動(dòng)構(gòu)造 (MoveConstructible) 和可移動(dòng)賦值 (MoveAssignable) 的要求淹真,但不滿足可復(fù)制構(gòu)造 (CopyConstructible) 或可復(fù)制賦值 (CopyAssignable) 的要求。
unique_ptr的使用
備注:make_unique 是C++14的新特性连茧,如果使用C++11編譯核蘸,請(qǐng)把make_unique改成std::unique_ptr<T>的形式巍糯。
#include <iostream>
#include <vector>
#include <memory>
#include <cstdio>
#include <fstream>
#include <cassert>
#include <functional>
struct B {
virtual void bar() { std::cout << "B::bar\n"; }
virtual ~B() = default;
};
struct D : B
{
D() { std::cout << "D::D\n"; }
~D() { std::cout << "D::~D\n"; }
void bar() override { std::cout << "D::bar\n"; }
};
// 消費(fèi) unique_ptr 的函數(shù)能以值或以右值引用接收它
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
p->bar();
return p;
}
void close_file(std::FILE* fp) { std::fclose(fp); }
int main()
{
std::cout << "unique ownership semantics demo\n";
{
auto p = std::make_unique<D>(); // p 是占有 D 的 unique_ptr
auto q = pass_through(std::move(p));
assert(!p); // 現(xiàn)在 p 不占有任何內(nèi)容并保有空指針
q->bar(); // 而 q 占有 D 對(duì)象
} // ~D 調(diào)用于此
std::cout << "Runtime polymorphism demo\n";
{
std::unique_ptr<B> p = std::make_unique<D>(); // p 是占有 D 的 unique_ptr
// 作為指向基類的指針
p->bar(); // 虛派發(fā)
std::vector<std::unique_ptr<B>> v; // unique_ptr 能存儲(chǔ)于容器
v.push_back(std::make_unique<D>());
v.push_back(std::move(p));
v.emplace_back(new D);
for(auto& p: v) p->bar(); // 虛派發(fā)
} // ~D called 3 times
std::cout << "Custom deleter demo\n";
std::ofstream("demo.txt") << 'x'; // 準(zhǔn)備要讀的文件
{
std::unique_ptr<std::FILE, void (*)(std::FILE*) > fp(std::fopen("demo.txt", "r"),
close_file);
if(fp) // fopen 可以打開失敗客扎;該情況下 fp 保有空指針
std::cout << (char)std::fgetc(fp.get()) << '\n';
} // fclose() 調(diào)用于此祟峦,但僅若 FILE* 不是空指針
// (即 fopen 成功)
std::cout << "Custom lambda-expression deleter demo\n";
{
std::unique_ptr<D, std::function<void(D*)>> p(new D, [](D* ptr)
{
std::cout << "destroying from a custom deleter...\n";
delete ptr;
}); // p 占有 D
p->bar();
} // 調(diào)用上述 lambda 并銷毀 D
std::cout << "Array form of unique_ptr demo\n";
{
std::unique_ptr<D[]> p{new D[3]};
} // 調(diào)用 ~D 3 次
}
運(yùn)行結(jié)果:
unique ownership semantics demo
D::D
D::bar
D::bar
D::~D
Runtime polymorphism demo
D::D
D::bar
D::D
D::D
D::bar
D::bar
D::bar
D::~D
D::~D
D::~D
Custom deleter demo
x
Custom lambda-expression deleter demo
D::D
D::bar
destroying from a custom deleter...
D::~D
Array form of unique_ptr demo
D::D
D::D
D::D
D::~D
D::~D
D::~D
智能指針使用總結(jié):
- 智能指針自己管理內(nèi)存的聲明周期;
- 智能指針在構(gòu)造是可以制定對(duì)應(yīng)了銷毀函數(shù)徙鱼;
進(jìn)階用法:
在上述的智能指針使用中宅楞,我們通過傳入delete的函數(shù),這里可以采用更優(yōu)雅的方式:通過結(jié)構(gòu)體的運(yùn)算符重載達(dá)到delete函數(shù)的效果
#include <iostream>
#include <memory>
using namespace std;
typedef struct _package{
unsigned char* data;
int length;
}package_t;
void release_package(package_t* package){
cout<<"release_package"<<endl;
if(!package){
return;
}
if(package->data){
delete[] package->data;
}
delete package;
}
struct package_destuctor{
void operator()(package_t* package){
release_package(package);
}
};
// unique_ptr 不能拷貝或者復(fù)制
int main(){
unique_ptr<package_t, decltype(release_package)*> ret1(new package_t(), release_package);
unique_ptr<package_t, package_destuctor> ret(new package_t());
return 0;
}
運(yùn)行結(jié)果:
兩種方式都正常release數(shù)據(jù)
release_package
release_package
進(jìn)階二
unique_ptr作為形參時(shí)袱吆,必須保證不能發(fā)生COPY
例如:
unique_ptr<T> uptr(new T);
show_ptr(uptr);
這種調(diào)用會(huì)出錯(cuò)厌衙,原因在于uptr作為形參時(shí),會(huì)發(fā)生copy绞绒,而unique_ptr不允許Copy迅箩。
#include <iostream>
#include <memory>
using namespace std;
class MyTest{
public:
MyTest(const string & name)
:_name(name){
cout<<"MyTest:"<<_name<<endl;
}
MyTest(const MyTest & another){
_name = another._name;
cout<<another._name<<"copyStruct "<<_name<<endl;
}
MyTest & operator =(const MyTest & another){
if(&another==this)
return *this;
this->_name=another._name;
cout<<another._name<<"copyAssin to "<<_name<<endl;
}
~MyTest(){
cout<<"~MyTest:"<<_name<<endl;
}
//private:
string _name;
};
//!例外:
//①返回一個(gè)即將被銷毀的uniptr
unique_ptr<MyTest> retDying(string param){
return unique_ptr<MyTest>(new MyTest(param));
}
//②返回一個(gè)局部對(duì)象;
unique_ptr<MyTest> retTemp(string param){
unique_ptr<MyTest> pTemp(new MyTest(param));
return pTemp;
}
//unique_ptr可以作為形參,必須保證不能發(fā)生copy
unique_ptr<int> show(unique_ptr<int> up){
cout<<*up<<endl;
return up;
}
//不能刪除unique中的指針,如果刪除,智能指針會(huì)報(bào)錯(cuò)
void release(unique_ptr<MyTest>& ptr){
if(!ptr){
cout<<"delete_ptr ptr is null"<<endl;
return;
}
auto p = ptr.get();
//delete p;
}
// unique_ptr 不能拷貝或者復(fù)制
int main(){
unique_ptr<MyTest> ret1 = retDying("dying");
cout<<(*ret1)._name<<endl;
unique_ptr<int> pCount(new int(10));
//unique_ptr可以作為形參,必須保證不能發(fā)生copy,pCount不能當(dāng)做參數(shù),可以使用轉(zhuǎn)移或者move
show(unique_ptr<int>(new int(10)));
show(move(pCount));
unique_ptr<MyTest> ret2 = retTemp("temp");
cout<<(*ret2)._name<<endl;
//如果傳ret,必須聲明為引用,如果不聲明引用,必須則不能使用ret1作為參數(shù), 可以u(píng)nique_ptr<int> retp(ret1.release())
release(ret1);
return 0;
}
結(jié)果如下:
MyTest:dying
dying
10
10
MyTest:temp
temp
~MyTest:temp
~MyTest:dying
unique_ptr之release重點(diǎn)聲明
unique_ptr中的release方法处铛,第一眼看過去,是釋放內(nèi)存拐揭,其實(shí)并不是撤蟆,并不是,并不是堂污,重要的事情說三遍
看unique_ptr的release函數(shù)聲明:
pointer release() noexcept;
其功能是當(dāng)前的智能指針釋放對(duì)持有指針的控制家肯,并返回持有的指針,其含義是:release之后盟猖,大爺不管了讨衣,返回的指針你自己玩吧, 千萬不要想當(dāng)然的認(rèn)為是釋放內(nèi)存式镐,只是釋放控制權(quán)7凑颉!娘汞!