智能指针是使用RAII方法对普通指针进行的封装, 所以智能指针实质是对象, 行为表现是指针.
RAII, Resource Acquisition Is Initialization, 资源获取就是初始化. RAII是一种管理资源、避免泄漏的惯用法. 通常的做法是对于一个对象而言, 我们在构造函数的时候申请空间, 而在析构函数(在离开作用域时调用)的时候释放空间.
使用智能指针可以方便的管理堆内存, 避免使用普通指针容易导致的堆内存未释放或二次释放等问题.
C++智能指针在C++11前是std::auto_ptr
, C++11后是std::shared_ptr
/std::unique_ptr
/std::weak_ptr
, 使用它们需要包含头文件<memory>
.
std::auto_ptr
std::auto_ptr
是C++11前提供的智能指针, 但从C++11开始, std::auto_ptr
开始被弃用, 被std::unique_ptr
替代.
std::auto_ptr
的问题在于允许隐式的所有权转让, 容易导致不易发现的各种错误, 比如空指针访问.
std::auto_ptr<std::string> ps1(new std::string("hello world"));
std::auto_ptr<std::string> ps2;
ps2 = ps1;
std::cout << ps1;
在ps2 = ps1
后ps1
的资源所有权已经释放, 不再指向任何内容, 后面再访问ps1
相当于在访问一个空指针.
std::unique_ptr
std::uniqur_ptr
在std::auto_ptr
的基础上禁止了拷贝和赋值, 这样上述std::auto_ptr
中的问题就不存在了, 因为在编译阶段就会报错.
另外, 容器不支持保存std::auto_ptr
, 但可以支持保存std::uniqur_ptr
.
std::uniqur_ptr
是应该优先选用的智能指针.
std::shared_ptr
std::shared_ptr
使用引用计数, 多个指针对象可以指向相同的内存. 每次std::shared_ptr
的拷贝, 内部引用计数加1; 每次std::shared_ptr
的析构, 内部引用计数减1; 当引用计数减为0时, 自动释放指向的内存. std::shared_ptr
内部引用计数是线程安全的, 但指向内存不是, 所以多线程使用std::shared_ptr
还是需要锁.
- 注意不要使用同一个原始指针初始化多个
std::shared_ptr
, 否则会造成同一内存的二次释放 - 注意避免循环引用,循环引用会导致堆内存无法正确释放,导致内存泄漏. 这是
std::shared_ptr
的最大的陷阱. - 另外,
std::shared_ptr
使用的引用计数是增加了内存消耗和复杂性的, 所以能用std::unique_ptr
时建议不要使用std::shared_ptr
.