C++11新特性之智能指针
前言
C/C++不像Java那样JVM有垃圾回收机制,需要使用者malloc
和new
分配内存,free
和delete
释放内存。一旦忘记释放内存,则会出现内存泄露问题。
在C++11标准中新增unique_ptr
、shared_ptr
和weak_ptr
这三个智能指针来自动回收堆分配的对象。C++98中的auto_ptr
也置为deprecated。下面总结下这四个指针的区别。
auto_ptr
在C++98开始推出auto_ptr管理裸指针,让开发者不需要手动释放指针内存,当生命周期结束时自动释放内存。但是auto_ptr在转移所有权会出现野指针导致崩溃。
void autoPtr(){
std::auto_ptr<int> auto_ptr1(new int (1));
std::auto_ptr<int> auto_ptr2 = auto_ptr1; // 转移控制权
*auto_ptr1 += 1; // 程序崩溃
// 由于auto_ptr1转移给auto_ptr2,auto_ptr1变为空指针,导致程序崩溃,可以通过get方法先判空
if (auto_ptr1.get()){ // 判空后执行
// do somethings
}
}
unique_ptr
C++11新出现unique_ptr
智能指针管理内存,从命名上可以知道,该智能指针智能被唯一一个对
象持有,与auto_ptr一样拥有唯一拥有控制权的特性,不同的是unique_ptr是没有复制构造函数的,只能通过显示std::move()
方法转移控制权。
#include <memory>
int main(int argc, char *argv[]) {
auto unique_ptr1 = std::unique_ptr<int>();
auto unique_ptr2 = unique_ptr1; // 直接复制,错误。编译出错
auto unique_ptr3 = std::move(unique_ptr1); // unique_ptr1转移控制权到unique_ptr3,unique_ptr1变为nullptr
return 0;
}
unique_ptr的应用场景
#include <memory>
#include <iostream>
class A {
public:
A() {}
~A() {}
};
class B {
public:
B() : a(new A()) {
}
void output() {
std::cout << "这是B对象" << std::endl;
};
~B() {
// 不释放a
}
private:
A *a;
};
int main(int argc, char *argv[]) {
// B析构函数中A对象不会释放,导致内存泄露
B *b = new B();
b->test();
// 使用unique_ptr会自动释放内存
auto unique_ptr1 = std::unique_ptr<B>(new B());
unique_ptr1->output();
return 0;
}
shared_ptr
相对比unique_ptr
,shared_ptr
支持复制,即多个对象共享同一块内存。采用引用计数的方式式实现内存的自动管理,当复制一次值+1,释放一次则值-1,直到值为0内存会被回收。
#include <iostream>
#include <memory>
using namespace std;
int main() {
// 推荐方式
shared_ptr<int> sharedPtr1(new int(5)); // 匿名函数
shared_ptr<int> sharedPtr2 = make_shared<int>(5); // 工厂模式
// 引用计数
std::shared_ptr<int> sharedPtr3 = sharedPtr1;
cout << sharedPtr1.use_count() << std::endl; // 输出为2
sharedPtr3.reset(); // 释放
cout << sharedPtr1.use_count() << endl; // 输出为1
return 0;
}
引用计数的方式引起循环引用导致无法被正常的释放,如下示例。
#include <iostream>
#include <memory>
using namespace std;
class Child;
class Parent {
public:
shared_ptr<Child> child;
};
class Child {
public:
shared_ptr<Parent> parent;
};
int main() {
shared_ptr<Parent> parentPtr = make_shared<Parent>();
shared_ptr<Child> childPtr = make_shared<Child>();
// 循环引用 导致内存永远不会被释放内存泄露
parentPtr->child = childPtr;
childPtr->parent = parentPtr;
return 0;
}
weak_ptr
在上一part中介绍得share_ptr
由于采用引用计数的方式,在循环引用时候时候use_count的值+1,导致释放后计数值依然为1。而weak_ptr
则用于解决此问题,share_ptr
赋值给weak_ptr
,不会引起引用计数值+1,只是在弱引用计数值+1,因此不会影响到share_ptr
的生命周期。
#include <iostream>
#include <memory>
using namespace std;
class Child;
class Parent {
public:
shared_ptr<Child> child;
};
class Child {
public:
weak_ptr<Parent> parent; // 改为weak_ptr不会导致强引用计数值+1
};
int main() {
shared_ptr<Parent> parentPtr = make_shared<Parent>();
shared_ptr<Child> childPtr = make_shared<Child>();
// 循环引用 导致内存永远不会被释放内存泄露
parentPtr->child = childPtr;
childPtr->parent = parentPtr;
return 0;
}