1.auto_ptr 被复制后,将失去原来所致资源的所有权;
2.scoped_ptr永远不能被复制或被赋值!scoped_ptr拥有它所指向的资源的所有权,并永远不会放弃这个所有权;
3.shared_ptr 是可以共享所有权的智能指针;
#include "stdafx.h"
//#include "windows.h"
#include <memory>
#include <iostream>
using std::tr1::shared_ptr;
class Foo
{
public:
Foo()
{
std::cout<<"new Foo()"<<std::endl;
i = 0;
}
~Foo()
{
std::cout<<"destroy Foo()"<<std::endl;
}
void print()
{
std::cout<<"Foo::print"<<std::endl;
}
int i;
};
void OutSharePtr(shared_ptr<Foo> &P)
{
Foo* p = new Foo();
shared_ptr<Foo> sp1(p);
std::cout<<sp1.use_count()<<"::"<<sp1.get()<<std::endl;
shared_ptr<Foo> sp2(sp1);
p->print();
sp2->print();
std::cout<<sp1.use_count()<<"::"<<sp1.get()<<std::endl;
P = sp2;
std::cout<<sp1.use_count()<<"::"<<sp1.get()<<std::endl;
}
void InSharePtr(shared_ptr<Foo> &p)
{
p->i += 100;
std::cout<<p.use_count()<<"::"<<p.get()<<std::endl;
}
#include <algorithm>
char upCase(char c)
{
return toupper(c);
}
int _tmain(int argc, _TCHAR* argv[])
{
shared_ptr<Foo> sp1(new Foo());
shared_ptr<Foo> sp2(sp1);
shared_ptr<Foo> sp3;
sp1->print();
sp2->print();
std::cout<<sp1.use_count()<<"::"<<sp1.get()<<std::endl;
std::cout<<sp3.use_count()<<"::"<<sp3.get()<<std::endl;
sp3.swap(sp1);
std::cout<<sp1.use_count()<<"::"<<sp1.get()<<std::endl;
std::cout<<sp3.use_count()<<"::"<<sp3.get()<<std::endl;
sp3.reset();
std::cout<<sp1.use_count()<<"::"<<sp1.get()<<std::endl;
sp2.reset();
std::cout<<sp1.use_count()<<"::"<<sp1.get()<<std::endl;
sp1.reset();
std::cout<<sp1.use_count()<<"::"<<sp1.get()<<std::endl;
return 0;
}
C++11 引入的智能指针通过自动管理动态内存,显著提升了代码的安全性和可维护性。以下是它们的核心内容:
一、智能指针的优点
- 自动内存管理
智能指针在作用域结束时自动释放内存,避免手动delete
导致的内存泄漏。 - 异常安全
即使发生异常,资源也会被正确释放,符合 RAII(资源获取即初始化)原则。 - 明确的资源所有权
通过所有权模型(独占、共享)清晰表达资源生命周期。 - 更安全的指针操作
禁止隐式类型转换,减少野指针和空指针解引用风险。
二、智能指针的种类
1. std::unique_ptr
- 特点:独占所有权,不可复制,只能移动(通过
std::move
转移所有权)。 - 适用场景:单一所有者,如工厂函数返回对象、局部资源管理。
- 优势:无额外开销,轻量高效。
- 示例:
cpp
复制
std::unique_ptr<int> ptr = std::make_unique<int>(42); // C++14+ 推荐 // 或 std::unique_ptr<int> ptr(new int(42));
2. std::shared_ptr
- 特点:共享所有权,通过引用计数管理资源,支持复制。
- 适用场景:多个对象需共享同一资源(如容器、多线程共享数据)。
- 注意:循环引用会导致内存泄漏,需配合
weak_ptr
解决。 - 优势:灵活共享资源,自动释放。
- 示例:
cpp
复制
std::shared_ptr<int> ptr1 = std::make_shared<int>(42); std::shared_ptr<int> ptr2 = ptr1; // 引用计数 +1
3. std::weak_ptr
- 特点:非拥有性观察者,不增加引用计数,需通过
lock()
获取临时shared_ptr
。 - 适用场景:打破
shared_ptr
循环引用(如双向链表、缓存)。 - 优势:解决内存泄漏问题,避免所有权混乱。
- 示例:
cpp
复制
std::shared_ptr<int> shared = std::make_shared<int>(42); std::weak_ptr<int> weak = shared; if (auto tmp = weak.lock()) { // 尝试获取 shared_ptr // 使用 tmp 访问资源 }
三、关键注意事项
- 优先使用
std::make_unique
和std::make_shared
- 更高效(
make_shared
合并内存分配),避免异常安全问题。
- 更高效(
- 避免原始指针的混用
- 智能指针应直接管理资源,避免与
new/delete
手动操作交叉使用。
- 智能指针应直接管理资源,避免与
- 循环引用问题
- 当两个
shared_ptr
互相引用时,需将其中一个改为weak_ptr
。
- 当两个
四、总结
- **
unique_ptr
**:独占资源,轻量高效。 - **
shared_ptr
**:共享资源,需警惕循环引用。 - **
weak_ptr
**:辅助shared_ptr
,解决循环引用。
合理使用智能指针可大幅减少内存管理错误,是现代 C++必备工具。
auto_ptr
和 scoped_ptr
是否属于 C++11?
-
auto_ptr
- C++98 标准:
auto_ptr
是 C++98 标准中引入的早期智能指针,用于管理动态内存。 - C++11 中的状态:在 C++11 中被标记为 已废弃(deprecated),并在 C++17 中正式移除。
- 原因:设计存在严重缺陷(如所有权转移的隐式行为),容易导致代码错误。
- C++98 标准:
-
scoped_ptr
- 非标准库组件:
scoped_ptr
并非 C++ 标准库的一部分,而是 Boost 库 中的一个智能指针(定义在<boost/scoped_ptr.hpp>
中)。 - C++11 中的状态:C++11 未引入此名称的智能指针,但功能类似的
unique_ptr
被纳入标准库。 - 特点:不可复制、不可移动,仅在作用域内有效,自动释放资源。
- 非标准库组件:
与 C++11 智能指针的区别
1. auto_ptr
vs unique_ptr
特性 | auto_ptr (C++98) | unique_ptr (C++11) |
---|---|---|
所有权模型 | 独占所有权,但通过隐式拷贝语义转移所有权。 | 独占所有权,禁止隐式拷贝,必须显式使用 std::move 转移所有权。 |
拷贝行为 | 拷贝时会将原指针置为 nullptr ,可能导致悬空指针。 | 拷贝构造函数和赋值运算符被删除,无法拷贝。 |
移动语义 | 无移动语义(C++11 前)。 | 支持移动语义(通过 std::move )。 |
安全性 | 易导致意外的所有权转移和内存泄漏。 | 更安全,所有权转移显式且无歧义。 |
适用场景 | C++98 代码,已过时。 | 现代 C++ 中替代 auto_ptr 。 |
示例对比:
cpp
复制
// auto_ptr(危险!)
auto_ptr<int> p1(new int(42));
auto_ptr<int> p2 = p1; // p1 变为 nullptr,p2 接管资源!
int* raw = p1.get(); // 返回 nullptr,解引用会导致崩溃!
// unique_ptr(安全)
unique_ptr<int> up1(new int(42));
unique_ptr<int> up2 = move(up1); // 显式转移所有权,up1 变为 nullptr
int* raw = up1.get(); // 返回 nullptr,但行为明确
2. scoped_ptr
vs unique_ptr
特性 | scoped_ptr (Boost) | unique_ptr (C++11) |
---|---|---|
标准库支持 | 非标准(Boost 库)。 | C++11 标准的一部分。 |
拷贝/移动语义 | 不可拷贝、不可移动。 | 不可拷贝,但支持移动语义。 |
灵活性 | 仅限作用域内使用,无法转移所有权。 | 支持通过 std::move 转移所有权。 |
功能扩展性 | 功能简单,无自定义删除器等高级特性。 | 支持自定义删除器、数组类型等。 |
内存开销 | 无额外开销。 | 无额外开销,与 scoped_ptr 类似。 |
核心区别:
scoped_ptr
是 Boost 提供的轻量级工具,适用于严格作用域内的资源管理,但无法转移所有权。unique_ptr
是 C++11 标准,功能更强大(支持移动语义、自定义删除器),是现代 C++ 的首选。
总结:为何 C++11 智能指针更优?
- 明确的 ownership 语义:
unique_ptr
通过禁止隐式拷贝、支持显式移动,彻底解决了auto_ptr
的所有权混乱问题。
- 异常安全与 RAII:
- C++11 智能指针严格遵循 RAII,与异常安全无缝结合。
- 功能扩展性:
- 支持自定义删除器(如文件句柄、锁)、数组类型(
unique_ptr<T[]>
)等。
- 支持自定义删除器(如文件句柄、锁)、数组类型(
- 标准库支持:
unique_ptr
和shared_ptr
是 C++11 标准的一部分,兼容性和可移植性更强。
何时使用旧智能指针?
- **
auto_ptr
**:仅在维护遗留 C++98 代码时可能遇到,应尽快迁移到unique_ptr
。 - **
scoped_ptr
**:如果项目已使用 Boost 库且无需移动语义,可继续使用,但新项目建议优先选择unique_ptr
。