C++中对于动态内存的使用非常严格,一次申请必须对应一次释放,否则将造成内存泄漏。这也要求程序员格外小心,比如如下示例:
void getStr() {
std::string * pstr = new std::string();//pstr为局部变量
*pstr = "Hello world";
....
return;
}
当该方法执行完毕后,局部变量pstr所占用内存自动释放,但其指向的内存则一直驻留。必须通过delete pstr
来释放,因此这里将造成内存泄漏。
还有一种情况就是当程序因异常而终止时:
#include <stdexcept>
void getStr() {
char * pch = new char[1024];
...
if (true)
throw logic_error("throw a exception");
delete [] pch;
}
虽然在这里没有忘记delete释放内存,但是当程序执行到throw时,将不再执行throw之后的语句,因此导致内存泄漏。针对于这个例子,虽然可以通过如下的方式避免这个问题,但依旧需要非常小心:
#include <stdexcept>
void getStr() {
char * pch = new char[1024];
...
if (true)
try{
throw logic_error("throw a exception");
} catch(...){
delete [] pch;
throw;
}
delete [] pch;
}
为了让程序员不再因内存泄漏而操心,C++11中提供了几个用于动态内存管理的智能指针。
智能指针的原理
如果在指针对象过期时,让它的析构函数删除它指向的内存,那不就避免内存的泄漏了吗?比如当自动变量pstr被销毁时,让他的析构函数可以释放它指向的内。
智能指针正是采用这种方式实现的,在智能指针类中,定义了类似指针的对象,然后将new
获得的地址赋给这个对象,当智能指针过期后,其析构函数将使用delete
来释放这块内存。
使用智能指针
智能指针模板类位于头文件memory
中,因此使用时必须包含该头文件。memory
中提供了四种智能指针模板类:auto_ptr
,unique_ptr
,shared_ptr
,weak_ptr
,其中auto_ptr
在C++11中被弃用,在C++17中被移除,因此主要学习其余三种。
unique_ptr
unique_ptr
类模板如下:
template<class T, class Deleter = std::default_delete<T>>
class unique_ptr;
template <class T,class Deleter>
class unique_ptr<T[], Deleter>;
因此,unique_ptr
有两个版本:
- 1.管理个对象(以 new 分配)
- 2.管理动态分配的对象数组(以 new[] 分配)
构造方法
unique_ptr
的构造方法有多个,但常用如下几个个:
constexpr unique_ptr() noexcept;
constexpr unique_ptr (nullptr_t) noexcept : unique_ptr() {
}
explicit unique_ptr(pointer p) noexcept;
unique_ptr
通过建立所有权概念来管理对象,也就是说,对于特定的对象,只能有一个智能指针可以拥有它。因此:
- 1.不能使用一个
unique_ptr
对象来初始化另一个unique_ptr
对象,其拷贝构造不可用:unique_ptr (const unique_ptr&) = delete
; - 2.不能使用一个
unique_ptr
对象来给另一个unique_ptr
赋值,其赋值运算符不可用:unique_ptr& operator= (const unique_ptr&) = delete
.
然而,当试图将一个unique_ptr
对象赋给另一个unique_ptr
时,如果源unique_ptr
是一个临时右值,则编译器允许这样做,如:
std::unique_ptr<Person> get_uptr(Person* p) {
std::unique_ptr<Person> temp (p);
return temp;
}
std::unique_ptr<Person> uptr4 = get_uptr(new Person("XiaoWang"));
这是通过移动构造来区分的。
如果需要将一个unique_ptr
对象赋给另一个unique_ptr
,则使用std::move()
函数;
成员函数
get()
:返回指向被管理对象的指针。reset()
:替换被管理对象;release()
: 返回一个指向被管理对象的指针,并释放所有权.swap()
:替换被管理对象;operator bool
:检查是否有关联的被管理对象.
下面为使用unique_ptr
示例:
#include <iostream>
#include <memory>
#include <string>
class Person
{
private:
std::string name;
public:
Person(const std::string& name):name(name){
std::cout <<