首页 > 代码库 > C++ Primer笔记8_动态内存_智能指针
C++ Primer笔记8_动态内存_智能指针
1.动态内存
C++中,动态内存管理是通过一对运算符完成的:new和delete。C语言中通过malloc与free函数来实现先动态内存的分配与释放。C++中new与delete的实现其实会调用malloc与free。
new分配:
分配变量空间:
int *a = new int; // 不初始化 int *b = new int(10); //初始化为10 string *str = new string(10, );
分配数组空间:
int *arr = new int[10];//分配的数组空间未初始化!
delete释放:
delete a; delete [] arr;//delete的对象是数组
2.智能指针
由于 C++ 语言没有自动内存回收机制,每次 new 出来的内存都要手动 delete。程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见。
用智能指针便可以有效缓解这类问题,本文主要讲解参见的智能指针的用法。包括:std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array、boost::weak_ptr、boost:: intrusive_ptr。
C++11新标准提供了两种智能指针,负责自动释放所指向的对象。shared_ptr允许多个指针指向同一个对象;unique_ptr则"独占"所指向的对象。标准库还定义了一个名为weak_ptr的伴随类;这三种类型都定义在memory头文件中。
对于编译器来说,智能指针实际上是一个栈对象,并非指针类型,在栈对象生命期即将结束时,智能指针通过析构函数释放有它管理的堆内存。所有智能指针都重载了“operator->”操作符,直接返回对象的引用,用以操作对象。访问智能指针原来的方法则使用“.”操作符。
访问智能指针包含的裸指针则可以用 get() 函数。由于智能指针是一个对象,所以if (my_smart_object)永远为真,要判断智能指针的裸指针是否为空,需要这样判断:if (my_smart_object.get())。
智能指针包含了 reset() 方法,如果不传递参数(或者传递 NULL),则智能指针会释放当前管理的内存。如果传递一个对象,则智能指针会释放当前对象,来管理新传入的对象。
2.1 auto_ptr
#include <iostream> #include <memory> using namespace std; class A { public: A(int n, string s):num(n), str(s) { } ~A() { cout << "~A : " << num << " " << str << endl; } void print() { cout << "num: " << num << ", str: " << str << endl; } void setNum(int n) { num = n; } void setString(string s) { str = s; } private: int num; string str; }; int main() { auto_ptr<A> my(new A(10, string(10, 'n'))); if(my.get()) { my->print(); (*my).setNum(20); my.get()->setString(string("SCOTT")); my->print(); } auto_ptr<A> my1 = my; cout << my.get() << "------" << my1.get()<< endl; //my->print(); segment default! return 0; }
运行结果:
num: 10, str: nnnnnnnnnn
num: 20, str: SCOTT
0------0x9cd2028 ——赋值后所有权转移到my1 ,原来的my相当于野指针!
~A : 20 SCOTT
上述程序最后如果加上
my.release();
my1.release();
则会发现析构函数不会被执行! 说明release函数不会释放对象。
总结:std::auto_ptr 可用来管理单个对象的对内存,但是,请注意如下几点:
(1) 尽量不要使用“operator=”。如果使用了,请不要再使用先前对象。
(2) 记住 release() 函数不会释放对象,仅仅归还所有权。
(3) std::auto_ptr 最好不要当成参数传递(读者可以自行写代码确定为什么不能)。
(4) 由于 std::auto_ptr 的“operator=”问题,有其管理的对象不能放入 std::vector 等容器中。
由于 std::auto_ptr 引发了诸多问题,一些设计并不是非常符合 C++ 编程思想,所以引发了下面 boost 的智能指针,boost 智能指针可以解决如上问题。
2.2 boost::scoped_ptr
boost::scoped_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件#include<boost/smart_ptr.hpp> 便可以使用。boost::scoped_ptr 跟 std::auto_ptr 一样,可以方便的管理单个堆内存对象,特别的是,boost::scoped_ptr 独享所有权,避免了 std::auto_ptr恼人的几个问题。
#include <iostream> #include <boost/smart_ptr.hpp> using namespace std; using namespace boost; class A { public: A(int n, string s):num(n), str(s) { } ~A() { cout << "~A : " << num << " " << str << endl; } void print() { cout << "num: " << num << ", str: " << str << endl; } void setNum(int n) { num = n; } void setString(string s) { str = s; } private: int num; string str; }; int main() { scoped_ptr<A> my(new A(10, string(10, 'n'))); if(my.get()) { my->print(); (*my).setNum(20); my.get()->setString(string("SCOTT")); my->print(); } // scoped_ptr<A> my1 = my; error! scoped_ptr 没有重载operator=,不会导致所有权转移 scoped_ptr也没有定义release函数 cout << my.get() << "------" << my.get()<< endl; return 0; }
2.3 boost::shared_ptr
boost::shared_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件#include<boost/smart_ptr.hpp> 便可以使用。在上面我们看到 boost::scoped_ptr 独享所有权,不允许赋值、拷贝,boost::shared_ptr 是专门用于共享所有权的,由于要共享所有权,其在内部使用了引用计数。boost::shared_ptr 也是用于管理单个堆内存对象的。
#include <iostream> #include <boost/smart_ptr.hpp> using namespace std; using namespace boost; class A { public: A(int n, string s):num(n), str(s) { } ~A() { cout << "~A : " << num << " " << str << endl; } void print() { cout << "num: " << num << ", str: " << str << endl; } void setNum(int n) { num = n; } void setString(string s) { str = s; } private: int num; string str; }; void test(shared_ptr<A> &test) { test->print(); return ; } int main() { shared_ptr<A> my(new A(10, string(10, 'n'))); if(my.get()) { my->print(); (*my).setNum(20); my.get()->setString(string("SCOTT")); my->print(); } cout << "my.use_count: " << my.use_count() << endl; test(my); cout << "my.use_count: " << my.use_count() << endl; // cout << my.get() << "------" << my1.get()<< endl; //my->print(); segment default! return 0; }运行结果:
num: 10, str: nnnnnnnnnn
num: 20, str: SCOTT
my.use_count: 1
num: 20, str: SCOTT
my.use_count: 1
~A : 20 SCOTT
boost::shared_ptr 也可以很方便的使用。并且没有 release() 函数。关键的一点,boost::shared_ptr 内部维护了一个引用计数,由此可以支持复制、参数传递等。boost::shared_ptr 提供了一个函数 use_count() ,此函数返boost::shared_ptr 内部的引用计数。可以看到在test函数返回后,引用计数又降低为1;当我们需要使用一个共享对象的时候,boost::shared_ptr 是再好不过的。
2.4 weak_ptr
boost::weak_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件#include<boost/smart_ptr.hpp> 便可以使用。
在讲 boost::weak_ptr 之前,让我们先回顾一下前面讲解的内容。似乎boost::scoped_ptr、boost::shared_ptr 这两个智能指针就可以解决所有单个对象内存的管理了,这儿还多出一个 boost::weak_ptr,是否还有某些情况我们没纳入考虑呢?
回答:有。首先 boost::weak_ptr 是专门为 boost::shared_ptr 而准备的。有时候,我们只关心能否使用对象,并不关心内部的引用计数。boost::weak_ptr 是 boost::shared_ptr 的观察者(Observer)对象,观察者意味着 boost::weak_ptr 只对 boost::shared_ptr 进行引用,而不改变其引用计数,当被观察的 boost::shared_ptr 失效后,相应的 boost::weak_ptr 也相应失效。
写到这,先告一段落,理论知识是参考网上的博文,然后自己实现一些demo加深理解。