Skip to content

Ch 23: 智能指针

  • 深入理解智能指针解决什么问题
  • 掌握 unique_ptr 独占所有权的语义和用法
  • 掌握 shared_ptr 共享所有权的引用计数机制
  • 掌握 weak_ptr 打破循环引用和缓存场景
  • 理解定制删除器和智能指针工厂
  • 学会用智能指针替代裸指针

23.1 Python 引用计数 vs C++ 智能指针

Section titled “23.1 Python 引用计数 vs C++ 智能指针”
import sys
a = [1, 2, 3] # refcount = 1
b = a # refcount = 2
print(sys.getrefcount(a)) # 2(包含临时引用)
del a # refcount = 1
del b # refcount = 0,对象被回收
# Python 的引用计数 + 垃圾回收器处理循环引用
#include <memory>
#include <iostream>
// 三种智能指针
std::unique_ptr<int> up; // 独占所有权,只能有一个所有者
std::shared_ptr<int> sp; // 共享所有权,多个指针共享同一个对象
std::weak_ptr<int> wp; // 弱引用,不增加引用计数
int main() {
// unique_ptr - 独占
auto up1 = std::make_unique<int>(42);
std::unique_ptr<int> up2 = std::move(up1); // 转移所有权
// up1 现在是空,up2 拥有指针
// shared_ptr - 共享
auto sp1 = std::make_shared<int>(42);
std::shared_ptr<int> sp2 = sp1; // 共享,引用计数 = 2
std::cout << "Ref count: " << sp1.use_count() << "\n"; // 2
// weak_ptr - 观察但不拥有
std::weak_ptr<int> wp = sp1;
std::cout << "Use count: " << wp.use_count() << "\n"; // 2(不增加)
// 检查 weak_ptr 是否有效
if (auto locked = wp.lock()) {
std::cout << "Value: " << *locked << "\n"; // 42
}
return 0;
}
场景推荐指针原因
单个所有者unique_ptr零开销,明确所有权
多个所有者shared_ptr引用计数
缓存/观察者weak_ptr不增加计数
禁止拷贝的资源unique_ptr独占语义
#include <memory>
#include <iostream>
int main() {
// 创建方式 1:make_unique(C++14)
auto p1 = std::make_unique<int>(42);
auto p2 = std::make_unique<int[]>(10); // 数组
// 创建方式 2:直接构造
std::unique_ptr<int> p3(new int(100));
// 解引用
std::cout << "Value: " << *p1 << "\n";
// 访问成员(如果是类指针)
struct Point {
int x, y;
};
auto p4 = std::make_unique<Point>(Point{1, 2});
std::cout << "Point: " << p4->x << ", " << p4->y << "\n";
// 检查是否为空
if (p1) {
std::cout << "p1 is not null\n";
}
// reset - 释放并可选重新指向
p1.reset(); // p1 现在是空
p1.reset(new int(50)); // p1 重新指向新对象
// release - 返回指针并释放所有权(不删除对象)
int* raw = p1.release();
delete raw; // 调用者负责删除
// get - 返回原始指针(不转让所有权)
p1 = std::make_unique<int>(30);
int* raw2 = p1.get(); // 不要 delete!
*raw2 = 100; // 可以通过原始指针修改
return 0;
}
#include <memory>
#include <iostream>
// ❌ 危险:裸指针 - 所有权不明确
class Node {
public:
Node(int v) : value(v) {}
void set_next(Node* n) { next = n; }
Node* get_next() const { return next; }
private:
int value;
Node* next;
};
void bad_function(Node* ptr) {
// ptr 的所有权归谁?需要删除吗?
}
void badCaller() {
Node* n = new Node(1);
bad_function(n); // 危险!可能泄漏
delete n; // 谁知道该不该删
}
// ✅ 安全:unique_ptr - 所有权明确
void good_function(std::unique_ptr<Node> ptr) {
// ptr 的所有权归我,函数结束自动删除
}
void goodCaller() {
auto n = std::make_unique<Node>(1);
good_function(std::move(n)); // 转移所有权
// n 现在是空
}
#include <memory>
#include <iostream>
#include <vector>
int main() {
// 移动 unique_ptr
auto p1 = std::make_unique<int>(42);
std::unique_ptr<int> p2 = std::move(p1);
// p1 现在是空,p2 拥有指针
// ✅ 可以放入 vector
std::vector<std::unique_ptr<int>> vec;
vec.push_back(std::make_unique<int>(1));
vec.push_back(std::make_unique<int>(2));
vec.push_back(std::make_unique<int>(3));
// ❌ 不能直接拷贝
// vec.push_back(p2); // ❌ 编译错误!
// 移动可以
vec.push_back(std::move(p2));
// 遍历(需要 const 引用)
for (const auto& up : vec) {
std::cout << *up << " ";
}
std::cout << "\n";
// 使用 std::move 移动整个容器
std::vector<std::unique_ptr<int>> vec2;
for (auto& up : vec) {
vec2.push_back(std::move(up));
}
return 0;
}
#include <memory>
#include <iostream>
int main() {
// C++14 推荐:make_unique 数组
auto arr = std::make_unique<int[]>(5);
for (int i = 0; i < 5; ++i) {
arr[i] = i * i;
}
// C++17 可以用 make_unique_for_overwrite
// auto arr2 = std::make_unique_for_overwrite<int[]>(5);
// ❌ 不推荐:直接 new[]
std::unique_ptr<int[]> arr3(new int[10]);
// shared_ptr 数组(C++17)
// std::shared_ptr<int[]> sp_arr = std::make_shared<int[]>(10);
// 遍历
for (int i = 0; i < 5; ++i) {
std::cout << arr[i] << " ";
}
std::cout << "\n";
return 0;
}
#include <memory>
#include <iostream>
int main() {
// 创建 make_shared(推荐)
auto sp1 = std::make_shared<int>(42);
auto sp2 = sp1; // 共享,引用计数 = 2
// 原始指针也能构造(不推荐)
int* raw = new int(100);
std::shared_ptr<int> sp3(raw); // 所有权转移给 sp3
// 注意:不要在原始指针还存在时删除它!
// use_count - 引用计数
std::cout << "Ref count: " << sp1.use_count() << "\n"; // 2
// unique - 是否是唯一所有者
std::cout << "sp1 unique: " << sp1.unique() << "\n"; // false
// reset - 释放当前指针
sp1.reset(); // sp1 变为空,引用计数 = 1
std::cout << "After reset, *sp2 = " << *sp2 << "\n"; // 42
// reset 并重新指向
sp2.reset(new int(99));
std::cout << "After reset2, *sp2 = " << *sp2 << "\n"; // 99
return 0;
}
#include <memory>
#include <iostream>
int main() {
auto sp = std::make_shared<int>(42);
std::cout << "Initial count: " << sp.use_count() << "\n"; // 1
// 拷贝构造
auto sp2 = sp;
std::cout << "After copy: " << sp.use_count() << "\n"; // 2
// 作为函数参数(拷贝)
void use_shared(std::shared_ptr<int>);
use_shared(sp);
std::cout << "After passing to function: " << sp.use_count() << "\n"; // 3
// 移动(不增加计数)
auto sp3 = std::move(sp);
std::cout << "After move:\n";
std::cout << " sp.use_count(): " << sp.use_count() << "\n"; // 0(sp 变为空)
std::cout << " sp3.use_count(): " << sp3.use_count() << "\n"; // 3
return 0;
}
void use_shared(std::shared_ptr<int> p) {
std::cout << " In function: " << p.use_count() << "\n";
}
#include <memory>
#include <string>
#include <iostream>
class Database {
public:
static std::shared_ptr<Database> create(const std::string& connection_string) {
// 私有构造函数,需要通过工厂创建
return std::shared_ptr<Database>(new Database(connection_string));
}
void query(const std::string& sql) {
std::cout << "Query: " << sql << "\n";
}
private:
explicit Database(const std::string& conn)
: connection_string_(conn) {
std::cout << "Connecting to: " << conn << "\n";
}
std::string connection_string_;
};
int main() {
// 通过工厂创建
auto db = Database::create("postgresql://localhost");
// 可以在多个地方共享
auto db2 = db; // 引用计数 = 2
db->query("SELECT * FROM users");
db2->query("SELECT * FROM orders");
return 0;
}
#include <memory>
#include <iostream>
struct Heavy {
int data[1000];
};
int main() {
// unique_ptr 只有一个指针大小
std::cout << "unique_ptr<int>: " << sizeof(std::unique_ptr<int>) << "\n";
// shared_ptr 两个指针大小(控制块 + 指针)
std::cout << "shared_ptr<int>: " << sizeof(std::shared_ptr<int>) << "\n";
// weak_ptr 也是一个指针大小
std::cout << "weak_ptr<int>: " << sizeof(std::weak_ptr<int>) << "\n";
// 但每个 shared_ptr 指向同一个控制块
auto sp1 = std::make_shared<int>(42);
auto sp2 = sp1; // 两个指针共享同一个控制块
return 0;
}
#include <memory>
#include <iostream>
int main() {
// 创建 weak_ptr
auto sp = std::make_shared<int>(42);
std::weak_ptr<int> wp = sp;
// use_count - 引用计数(不增加)
std::cout << "Use count: " << wp.use_count() << "\n"; // 1
// expired - 检查对象是否已删除
if (!wp.expired()) {
std::cout << "Object still exists\n";
}
// lock - 获取 shared_ptr(如果对象还存在)
if (auto locked = wp.lock()) {
std::cout << "Value: " << *locked << "\n";
} else {
std::cout << "Object expired\n";
}
// 原始 shared_ptr 销毁后,weak_ptr 过期
sp.reset();
if (wp.expired()) {
std::cout << "Object expired after reset\n";
}
// lock 再次调用会失败
if (auto locked = wp.lock()) {
std::cout << "Value: " << *locked << "\n";
} else {
std::cout << "Cannot lock expired weak_ptr\n";
}
return 0;
}
#include <memory>
#include <iostream>
// ❌ 危险:循环引用导致内存泄漏
class NodeBad {
public:
NodeBad(int v) : value(v) {}
std::shared_ptr<NodeBad> next;
int value;
};
// 问题:两个 shared_ptr 互相引用,引用计数永远不为 0
void bad_example() {
auto n1 = std::make_shared<NodeBad>(1);
auto n2 = std::make_shared<NodeBad>(2);
n1->next = n2; // n2 的引用计数 = 1
n2->next = n1; // n1 的引用计数 = 1
// 即使离开作用域,引用计数也不会到 0!
}
// ✅ 正确:用 weak_ptr 打破循环
class NodeGood {
public:
NodeGood(int v) : value(v) {}
std::shared_ptr<NodeGood> next;
std::weak_ptr<NodeGood> prev; // 改为 weak_ptr
int value;
};
void good_example() {
auto n1 = std::make_shared<NodeGood>(1);
auto n2 = std::make_shared<NodeGood>(2);
n1->next = n2; // n2 引用计数 = 1
n2->prev = n1; // n1 引用计数仍为 1(prev 是 weak)
// weak_ptr 不增加引用计数,所以能正常释放
}
// 访问 weak_ptr
void access_prev(const std::weak_ptr<NodeGood>& wp) {
if (auto p = wp.lock()) {
std::cout << "Prev value: " << p->value << "\n";
} else {
std::cout << "Prev expired\n";
}
}
#include <memory>
#include <unordered_map>
#include <string>
#include <iostream>
template<typename Key, typename Value>
class Cache {
private:
struct Entry {
std::shared_ptr<Value> data;
std::weak_ptr<Value> cache_entry; // 反向引用
};
std::unordered_map<Key, Entry> cache_;
std::size_t max_size_;
public:
explicit Cache(std::size_t max_size) : max_size_(max_size) {}
void put(const Key& key, std::shared_ptr<Value> value) {
if (cache_.size() >= max_size_) {
// 删除过期条目
cleanup_expired();
}
cache_[key] = {value, value};
}
std::shared_ptr<Value> get(const Key& key) {
auto it = cache_.find(key);
if (it == cache_.end()) {
return nullptr;
}
auto sp = it->second.cache_entry.lock();
if (!sp) {
cache_.erase(it);
return nullptr;
}
return sp;
}
private:
void cleanup_expired() {
for (auto it = cache_.begin(); it != cache_.end(); ) {
if (it->second.cache_entry.expired()) {
it = cache_.erase(it);
} else {
++it;
}
}
}
};
struct Data {
std::string content;
Data(const std::string& c) : content(c) {}
~Data() { std::cout << "Data destroyed\n"; }
};
int main() {
Cache<int, Data> cache(3);
cache.put(1, std::make_shared<Data>("One"));
cache.put(2, std::make_shared<Data>("Two"));
cache.put(3, std::make_shared<Data>("Three"));
// 获取
if (auto d = cache.get(1)) {
std::cout << "Data: " << d->content << "\n";
}
// 清除引用后,数据可能过期
cache.get(1).reset();
cache.get(2).reset();
// 放入新数据触发清理
cache.put(4, std::make_shared<Data>("Four"));
return 0;
}
#include <memory>
#include <iostream>
#include <string>
class Widget {
public:
Widget() : data_(std::make_unique<int>(0)) {}
Widget(int v) : data_(std::make_unique<int>(v)) {}
int& value() { return *data_; }
int value() const { return *data_; }
// 转移所有权
void set_data(std::unique_ptr<int> new_data) {
data_ = std::move(new_data);
}
// 释放所有权(返回)
std::unique_ptr<int> release_data() {
return std::move(data_);
}
private:
std::unique_ptr<int> data_;
};
int main() {
Widget w(42);
std::cout << "Value: " << w.value() << "\n";
w.value() = 100;
std::cout << "New value: " << w.value() << "\n";
// 转移所有权
auto new_data = std::make_unique<int>(200);
w.set_data(std::move(new_data));
std::cout << "After set: " << w.value() << "\n";
// new_data 现在是空
if (!new_data) {
std::cout << "new_data is now empty\n";
}
return 0;
}
#include <memory>
#include <iostream>
#include <string>
#include <vector>
class DataBuffer {
public:
explicit DataBuffer(std::size_t size)
: data_(std::make_shared<std::vector<double>>(size)) {}
void fill(double value) {
std::fill(data_->begin(), data_->end(), value);
}
const auto& get_data() const { return *data_; }
std::size_t size() const { return data_->size(); }
// 获取共享指针的副本(用于共享)
std::shared_ptr<std::vector<double>> get_shared() const {
return data_;
}
private:
std::shared_ptr<std::vector<double>> data_;
};
int main() {
DataBuffer buffer(10);
buffer.fill(3.14);
// 多个地方可以共享同一个数据
auto buffer2 = buffer; // 拷贝 shared_ptr
std::cout << "Buffer size: " << buffer.size() << "\n";
std::cout << "Buffer2 shares data: "
<< (buffer.get_shared() == buffer2.get_shared()) << "\n";
return 0;
}
#include <memory>
#include <iostream>
#include <fstream>
// 文件删除器
struct FileDeleter {
void operator()(std::FILE* f) const {
if (f) {
std::fclose(f);
std::cout << "File closed\n";
}
}
};
// 数组删除器
struct ArrayDeleter {
template<typename T>
void operator()(T* p) const {
delete[] p;
std::cout << "Array deleted\n";
}
};
int main() {
// 自定义删除器
using FilePtr = std::unique_ptr<std::FILE, FileDeleter>;
FilePtr file(std::fopen("test.txt", "w"));
if (file) {
std::fputs("Hello", file.get());
}
// 离开作用域自动关闭文件
// Lambda 删除器
auto deleter = [](int* p) {
std::cout << "Deleting int: " << *p << "\n";
delete p;
};
std::unique_ptr<int, decltype(deleter)> p(new int(42), deleter);
// shared_ptr 定制删除器
std::shared_ptr<int> sp(new int(100), [](int* p) {
std::cout << "Custom delete: " << *p << "\n";
delete p;
});
return 0;
}
#include <memory>
#include <iostream>
int main() {
// shared_ptr 支持多种删除器
std::shared_ptr<int> sp1(new int(10), [](int* p) {
std::cout << "Lambda delete: " << *p << "\n";
delete p;
});
// shared_ptr 对数组支持不佳(C++17)
// 使用 custom deleter
std::shared_ptr<int[]> sp2(new int[5], [](int* p) {
delete[] p;
});
// 释放
sp1.reset();
sp2.reset();
// 注意:删除器类型是 shared_ptr 类型的一部分
// std::shared_ptr<int> 和 std::shared_ptr<int, Deleter> 是不同类型
return 0;
}
#include <memory>
#include <iostream>
class Bad {
public:
Bad() { std::cout << "Bad constructed\n"; }
~Bad() { std::cout << "Bad destroyed\n"; }
};
int main() {
// ❌ 错误 1:不要用同一个原始指针创建多个 shared_ptr
Bad* raw = new Bad();
std::shared_ptr<Bad> sp1(raw);
std::shared_ptr<Bad> sp2(raw); // ❌ 危险!引用计数会出错
// 两个 shared_ptr 各自有控制块,会导致双重删除!
// ✅ 正确:用一个 shared_ptr 拷贝构造另一个
auto sp3 = std::make_shared<Bad>(); // 或从 sp1 拷贝
// ❌ 错误 2:unique_ptr 作为参数后还使用
auto up = std::make_unique<int>(42);
process(std::move(up));
// if (up) { *up; } // ❌ up 已经是空!
// ❌ 错误 3:在容器中存储 unique_ptr 后移动
std::vector<std::unique_ptr<int>> vec;
vec.push_back(std::make_unique<int>(1));
auto up2 = std::move(vec[0]);
// vec[0] 现在是空的!
// ✅ 正确做法:先取出再移动
auto up3 = std::move(vec[0]); // OK
// 或
vec[0].reset(); // 显式清空
return 0;
}
#include <memory>
#include <iostream>
struct MyStruct {
int value;
MyStruct(int v) : value(v) { std::cout << "MyStruct(" << value << ")\n"; }
~MyStruct() { std::cout << "~MyStruct(" << value << ")\n"; }
};
// ✅ 正确 1:工厂函数返回智能指针
std::unique_ptr<MyStruct> create_struct(int v) {
return std::make_unique<MyStruct>(v);
}
// ✅ 正确 2:容器存储 unique_ptr
void use_container() {
std::vector<std::unique_ptr<MyStruct>> vec;
vec.push_back(std::make_unique<MyStruct>(1));
vec.push_back(std::make_unique<MyStruct>(2));
for (const auto& s : vec) {
std::cout << s->value << " ";
}
std::cout << "\n";
}
// ✅ 正确 3:使用 shared_ptr 共享数据
void share_data() {
auto data = std::make_shared<std::vector<int>>();
data->push_back(1);
auto copy = data; // 共享引用
copy->push_back(2);
std::cout << "Size: " << data->size() << "\n"; // 2
}
#include <memory>
#include <vector>
#include <stack>
#include <iostream>
template<typename T>
class ObjectPool {
public:
// 预分配对象
explicit ObjectPool(std::size_t initial_size) {
pool_.reserve(initial_size);
for (std::size_t i = 0; i < initial_size; ++i) {
pool_.push_back(std::make_unique<T>());
}
}
// 获取对象(从池中取或创建新的)
std::shared_ptr<T> acquire() {
std::shared_ptr<T> obj;
if (!pool_.empty()) {
// 从池中取出
obj = std::shared_ptr<T>(
pool_.back().release(),
[this](T* p) {
pool_.push_back(std::unique_ptr<T>(p));
}
);
} else {
// 创建新的
obj = std::shared_ptr<T>(
new T(),
[this](T* p) {
pool_.push_back(std::unique_ptr<T>(p));
}
);
}
return obj;
}
std::size_t available() const { return pool_.size(); }
private:
std::vector<std::unique_ptr<T>> pool_;
};
struct Connection {
int id;
bool active = false;
Connection() : id(next_id_++) {
std::cout << "Connection " << id << " created\n";
}
~Connection() {
std::cout << "Connection " << id << " destroyed\n";
}
void connect() { active = true; }
void disconnect() { active = false; }
private:
static int next_id_;
};
int Connection::next_id_ = 0;
int main() {
ObjectPool<Connection> pool(3);
std::cout << "Available: " << pool.available() << "\n";
// 获取连接
auto conn1 = pool.acquire();
auto conn2 = pool.acquire();
auto conn3 = pool.acquire();
std::cout << "After acquiring 3, available: " << pool.available() << "\n";
conn1->connect();
conn1.reset(); // 归还到池
std::cout << "After returning conn1, available: " << pool.available() << "\n";
auto conn4 = pool.acquire();
std::cout << "Acquired conn4\n";
return 0;
}
智能指针特点适用场景
unique_ptr独占所有权,零开销单个所有者,不需要拷贝
shared_ptr共享所有权,引用计数多个所有者共享数据
weak_ptr弱引用,不增加计数打破循环引用、缓存、观察者

使用原则:

  • 优先用 unique_ptr,需要共享时用 shared_ptr
  • 用 make_unique 和 make_shared 创建
  • 不要用同一个原始指针创建多个 shared_ptr
  • weak_ptr 用于打破循环和缓存

下章预告:ch24 学习移动语义,理解右值引用和移动构造。