0.指针的危害
- 指针未初始化
- 野指针
- 内存泄漏
1.智能指针分类
智能指针 | 进入C++标准的版本 | 头文件 | boost头文件 | 说明 |
---|---|---|---|---|
auto_ptr |
C++03 | memory |
<boost/auto_ptr.hpp> |
尽量避免使用 |
unique_ptr |
C++11 | memory |
<boost/unique_ptr.hpp> |
管理单个堆内存对象,独享所有权,不允许赋值和拷贝,不能用于管理数组对象 |
shared_ptr |
C++11 | memory |
<boost/shared_ptr.hpp> |
引用计数智能指针,共享所有权 |
weak_ptr |
C++11 | memory |
<boost/weak_ptr.hpp> |
shared_ptr的观察者,只进行引用而不改变引用计数,用来解决shared_ptr的循环引用问题 |
scoped_ptr |
- | - | <boost/scoped_ptr.hpp> |
作用域智能指针,功能与unique_ptr相似。 |
- 本质:
将指针封装为类对象成员,并在析构函数里删除指针指向的内存。
- 不同:
auto_ptr
、unique_ptr
、scoped_ptr
马上删除。shared_ptr
计数为0删除。weak_ptr
不删除。
智能指针是来解决指针危害的。
1. auto_ptr
-
作用
对作用域内的动态分配对象的自动释放
-
基本类型
#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可以看到没有内存泄露,指针可以被正常释放。
- 缺陷
- 两个
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
不能管理数组指针
#include <memory>
using namespace std;
int main(){
int*pa=new int[10];
auto_ptr<int>ap(pa);
}
auto_ptr
被拷贝或被赋值后,失去对原指针的管理.这种情况被称为指针所有权传递。
赋值的情况
#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容器中的元素经常要支持拷贝,赋值等操作。
#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
循环引用问题
#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
循环引用问题
#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
主要用来协助shared_ptr
。不参与引用计数,但是有以下好处:
- 打破递归的依赖关系
- 使用一个共享的资源但是不要所有权,不添加引用计数
- 避免悬空指针。
- 方法一
boost::shared_ptr<std::string> sp(new std::string("method1");
// 从shared_ptr构建出来
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构建出来
boost::weak_ptr<std::string>wp(sp);
// 再从shared_ptr获取回去
boost::shared_ptr<std::string> p(wp);
总结
智能指针的特性
智能指针 | 管理同一个对象 | 可拷贝 | 可复制 | 所有权 | 成为容器元素 | 管理数组指针 |
---|---|---|---|---|---|---|
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 |
虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅是一种编译期的解决方案。
如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的。
因此,不要认为只要使用了智能指针便能杜绝内存泄漏。
内存管理技术
实践
有关智能指针的使用规则
- 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
来管理独占所有权的资源
条款19: 使用std::shared_ptr
来管理共享所有权的资源
条款20: 使用std::weak_ptr
替换会造成指针悬挂的类std::shared_ptr
指针
条款21: 比起直接使用new
优先使用std::make_unique
和std::make_shared
简单实现智能指针
// #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;}
};
template<class T>
class shared_ptr{
struct Data{
T* ptr;
size_t count;
Data(T* ptr,size_t count):ptr(ptr),count(count){
}
};
Data* data;
public:
// explicit 取消单参构造函数等号初始化,不允许隐式转换到该对象
explicit shared_ptr(T* ptr):data(new Data(ptr,1)){
}
~shared_ptr(){
data->count--;
if(data->count == 0){
delete data->ptr;
delete data;
}
}
shared_ptr(const shared_ptr& ptr){
data = ptr.data;
data->count++;
}
shared_ptr& operator=(const shared_ptr& ptr){
if(this == &ptr || data == ptr.data) return *this;
data->count--;
if(data->count == 0){
delete data->ptr;
delete data;
}
data = ptr.data;
data->count++;
}
T& operator*(){
return *data->ptr;
}
T* operator->(){
return data->ptr;
}
bool operator==(const shared_ptr& ptr)const{
return data->ptr == ptr.data->ptr;
}
bool operator!=(const shared_ptr& ptr)const{
return data->ptr != ptr.data->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();
}