指針的危害
- 指針未初始化
- 野指針
- 內(nèi)存泄漏
參考閱讀C/C++指針使用常見的坑
智能指針分類
| 智能指針 | 進入C++標準的版本 | STL頭文件 | boost頭文件 | 說明 |
|---|---|---|---|---|
auto_ptr |
C++03 | <memory> |
<boost/auto_ptr.hpp> |
盡量避免使用 |
unique_ptr |
C++11 | <memory> |
<boost/unique_ptr.hpp> |
管理單個堆內(nèi)存對象,獨享所有權(quán),不允許賦值和拷貝,不能用于管理數(shù)組對象 |
shared_ptr |
C++11 | <memory> |
<boost/shared_ptr.hpp> |
引用計數(shù)智能指針,共享所有權(quán) |
weak_ptr |
C++11 | <memory> |
<boost/weak_ptr.hpp> |
shared_ptr的觀察者,只進行引用而不改變引用計數(shù),用來解決shared_ptr的循環(huán)引用問題 |
scoped_ptr |
- | - | <boost/scoped_ptr.hpp> |
作用域智能指針,功能與unique_ptr相似。 |
- 本質(zhì):
將指針封裝為類對象成員,并在析構(gòu)函數(shù)里刪除指針指向的內(nèi)存。 - 不同:
-
auto_ptr、unique_ptr、scoped_ptr馬上刪除。 -
shared_ptr計數(shù)為0刪除。 -
weak_ptr不刪除。
-
智能指針是來解決指針危害的。
1. auto_ptr
- 作用
對作用域內(nèi)的動態(tài)分配對象的自動釋放 - 基本類型
#include <iostream>
#include <memory>
using namespace std;
int main(){
int* pn = new int(10);
auto_ptr<int> ap(pn);
cout << *ap << endl;
}
- 類類型
#include <iostream>
#include <memory>
using namespace std;
class Test{
public:
Test(){cout << __func__ << endl;}
~Test(){cout << __func__ << endl;}
void Func(){cout << __func__ << endl;}
};
int main(){
Test* pt = new Test;
auto_ptr<Test> apt(pt);
apt->Func();
}
通過valgrind可以看到?jīng)]有內(nèi)存泄露,指針可以被正常釋放。
- 缺陷
- 兩個
auto_ptr不能同時擁有同一個對象
#include <memory>
using namespace std;
int main(){
int* pn = new int(10);
auto_ptr<int> ap1(pn);
auto_ptr<int> ap2(pn);
}
-
auto_ptr不能管理數(shù)組指針
#include <memory>
using namespace std;
int main(){
int*pa=new int[10];
auto_ptr<int>ap(pa);
}
-
auto_ptr被拷貝或被賦值后,失去對原指針的管理.這種情況被稱為指針所有權(quán)傳遞。
賦值的情況
#include <iostream>
#include <memory>
using namespace std;
int main(){
int*p = new int(10);
auto_ptr<int> ap1(p);
cout<< *ap1 <<endl;
auto_ptr<int> ap2=ap1;
cout<< *ap1 <<endl;
}
拷貝的情況
#include <iostream>
#include <memory>
using namespace std;
void Func(auto_ptr<int> ap){
cout << *ap << endl;
}
int main(){
int*p = new int(10);
auto_ptr<int> ap(p);
cout<< *ap <<endl;
Func(ap);
cout<< *ap <<endl;
}

-
auto_ptr不能作為容器對象,因為STL容器中的元素經(jīng)常要支持拷貝,賦值等操作。
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
int main(){
int*p = new int(10);
auto_ptr<int> ap(p);
vector<auto_ptr<int> > vec;
vec.push_back(ap);
}
2. unique_ptr
- 作用
代替auto_ptr,不復制與賦值。
3. scoped_ptr
- 作用
與unique_ptr相似,在本作用域中使用,不能復制與賦值。

4. shared_ptr
循環(huán)引用問題
#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
class parent;
class children;
class parent {
public:
~parent() { std::cout <<"destroying parent\n"; }
public:
boost::shared_ptr<children> children;
};
class children {
public:
~children() { std::cout <<"destroying children\n"; }
public:
boost::shared_ptr<parent> parent;
};
void test(){
boost::shared_ptr<parent> father(new parent());
boost::shared_ptr<children> son(new children);
father->children = son;
son->parent = father;
}
void main(){
std::cout<<"begin test...\n";
test();
std::cout<<"end test.\n";
}
5. weak_ptr
解決shared_ptr循環(huán)引用問題
#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
using namespace boost;
class parent;
class children;
class parent {
public:
~parent() { std::cout <<"destroying parent\n"; }
public:
shared_ptr<children> children;
};
class children {
public:
~children() { std::cout <<"destroying children\n"; }
public:
weak_ptr<parent> parent;
};
void test(){
shared_ptr<parent> father(new parent());
shared_ptr<children> son(new children);
father->children = son;
son->parent = father;
}
void main(){
std::cout<<"begin test...\n";
test();
std::cout<<"end test.\n";
}
智能指針weak_ptr主要用來協(xié)助shared_ptr。不參與引用計數(shù),但是有以下好處:
1 打破遞歸的依賴關系
2 使用一個共享的資源但是不要所有權(quán),不添加引用計數(shù)
3 避免懸空指針。
- 方法一
boost::shared_ptr<std::string> sp(new std::string("method1"); // 從shared_ptr構(gòu)建出來 boost::weak_ptr<std::string>wp(sp); // 再從shared_ptr獲取回去 boost::shared_ptr<std::string> p = wp.lock(); - 方法二
// 方法二 boost::shared_ptr<std::string> sp(new std::string("method1"); // 從shared_ptr構(gòu)建出來 boost::weak_ptr<std::string>wp(sp); // 再從shared_ptr獲取回去 boost::shared_ptr<std::string> p(wp);
總結(jié)
智能指針的特性
| 智能指針 | 管理同一個對象 | 可拷貝 | 可復制 | 所有權(quán) | 成為容器元素 | 管理數(shù)組指針 |
|---|---|---|---|---|---|---|
auto_ptr |
NG | OK | OK | 傳遞 | NG | NG |
unique_ptr |
NG | NG | NG | 獨享 | NG | NG |
scoped_ptr |
NG | NG | NG | 獨享 | NG | NG |
shared_ptr |
NG | OK | OK | 共享 | OK | NG |
weak_ptr |
NG | OK | OK | 共享 | OK | NG |
雖然通過弱引用指針可以有效的解除循環(huán)引用,但這種方式必須在程序員能預見會出現(xiàn)循環(huán)引用的情況下才能使用,也可以是說這個僅僅是一種編譯期的解決方案。
如果程序在運行過程中出現(xiàn)了循環(huán)引用,還是會造成內(nèi)存泄漏的。
因此,不要認為只要使用了智能指針便能杜絕內(nèi)存泄漏。
內(nèi)存管理技術





實踐
有關智能指針的使用規(guī)則
- Effective C++
條款17:以獨立語句將newed對象置于智能指針 - Effective STL
條款8:永不建立auto_ptr的容器 - Exceptional C++
條款37:auto_ptr - More Exceptional C++
條款21:未管理指針存在的問題,之二:使用auto_ptr?
條款29:使用auto_ptr
條款30:智能指針成員,之一:auto_ptr存在的問題 - Effective Modern C++
條款18: 使用std::unique_ptr來管理獨占所有權(quán)的資源
條款19: 使用std::shared_ptr來管理共享所有權(quán)的資源
條款20: 使用std::weak_ptr替換會造成指針懸掛的類std::shared_ptr指針
條款21: 比起直接使用new優(yōu)先使用std::make_unique和std::make_shared
簡單實現(xiàn)智能指針
// #include <memory>
#include <iostream>
using namespace std;
// auto_ptr C++03 C++11以后被廢棄
// unique_ptr C++11 ~ scope_ptr boost
// shared_ptr C++11/boost
// weak_ptr C++11/boost
namespace miniSTL{
template<typename T>
class auto_ptr{
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef const reference const_reference;
private:
pointer ptr;
public:
auto_ptr(pointer p):ptr(p){}
auto_ptr(const auto_ptr& p):ptr(p.ptr){
const_cast<auto_ptr&>(p).ptr = NULL; //
}
auto_ptr& operator=(const auto_ptr& p){
if(this == &p) return *this;
delete ptr;
ptr = p.ptr;
const_cast<auto_ptr&>(p).ptr = NULL; //
}
~auto_ptr(){delete ptr;}
reference operator*(){return *ptr;}
const_reference operator*()const{return *ptr;}
pointer operator->(){return ptr;}
const pointer operator->()const{return ptr;}
};
template<typename T>
class unique_ptr{
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef const reference const_reference;
private:
pointer ptr;
public:
unique_ptr(pointer p):ptr(p){}
unique_ptr(const unique_ptr& p) = delete;
unique_ptr& operator=(const unique_ptr& p) = delete;
~unique_ptr(){delete ptr;}
reference operator*(){return *ptr;}
const_reference operator*()const{return *ptr;}
pointer operator->(){return ptr;}
const pointer operator->()const{return ptr;}
};
};
using namespace miniSTL;
void Func(auto_ptr<int> ap){
cout << *ap << endl;
}
void Func(unique_ptr<int> up){
cout << *up << endl;
}
void Func(shared_ptr<int> sp){
cout << *sp << endl;
}
class IntegerPtr{
shared_ptr<int> ptr;
public:
IntegerPtr(int n):ptr(new int(n)){}
void Print(){
cout << *ptr << endl;
}
};
int main(){
auto_ptr<int> ap(new int(100));
// Func(ap);
// auto_ptr<int> ap2 = ap;
cout << *ap << endl;
unique_ptr<int> up(new int(100));
// Func(up);
// unique_ptr<int> up2 = up;
cout << *up << endl;
shared_ptr<int> sp(new int(110));
Func(sp);
shared_ptr<int> sp2 = sp;
cout << *sp2 << endl;
cout << *sp << endl;
// 空指針解引用
// int* p = NULL;
// cout << *p << endl;
IntegerPtr ptr(200);
ptr.Print();
IntegerPtr ptr2 = ptr;
ptr2.Print();
}