当前位置: 首页 > news >正文

More Effective C++ 条款29:引用计数

More Effective C++ 条款29:引用计数


核心思想引用计数是一种通过跟踪对象被引用次数来管理资源生命周期的技术,它在多个客户端需要共享相同资源时,可以避免不必要的拷贝,提高效率并简化资源管理。

🚀 1. 问题本质分析

1.1 资源共享的挑战

  • 不必要的拷贝:多个客户端需要相同数据时,深拷贝成本高昂
  • 资源释放时机:难以确定何时可以安全释放共享资源
  • 内存开销:大量相似对象重复存储相同内容浪费内存
  • 一致性维护:多个副本需要保持同步更新

1.2 引用计数的核心需求

  • 共享而非拷贝:允许多个对象共享同一份资源
  • 自动生命周期管理:当最后一个使用者释放资源时自动清理
  • 写时复制(Copy-on-Write):只在必要时创建副本
  • 内存效率:减少重复数据存储
// 基础示例:简单引用计数实现
class String {
public:String(const char* initValue = "") {init(initValue);}String(const String& rhs) {// 共享同一份数据data = rhs.data;data->addReference();}~String() {if (data->removeReference() == 0) {delete data;}}String& operator=(const String& rhs) {if (this != &rhs) {if (data->removeReference() == 0) {delete data;}data = rhs.data;data->addReference();}return *this;}const char& operator[](int index) const {return data->chars[index];}// 非const版本需要写时复制char& operator[](int index) {if (data->isShared()) {StringValue* newData = new StringValue(data->chars);data->removeReference();data = newData;}data->markUnshareable();return data->chars[index];}private:// 引用计数数据结构struct StringValue {size_t refCount;char* chars;bool shareable;StringValue(const char* initValue) : refCount(1), shareable(true) {chars = new char[strlen(initValue) + 1];strcpy(chars, initValue);}~StringValue() {delete[] chars;}void addReference() {++refCount;}size_t removeReference() {return --refCount;}bool isShared() const {return refCount > 1;}void markUnshareable() {shareable = false;}};StringValue* data;void init(const char* initValue) {data = new StringValue(initValue);}
};

📦 2. 问题深度解析

2.1 写时复制(Copy-on-Write)优化

// 增强的写时复制实现
class COWString {
public:// ... 构造函数、析构函数、拷贝操作同上// 安全的非const访问char& operator[](size_t index) {// 如果被共享,创建副本if (data->isShared()) {// 写时复制:创建新副本StringValue* newData = new StringValue(data->chars);data->removeReference();data = newData;}// 标记为不可共享(因为可能被修改)data->markUnshareable();return data->chars[index];}// 提供const版本避免不必要的复制const char& operator[](size_t index) const {return data->chars[index];}// 批量修改操作也需要检查共享void append(const char* str) {if (data->isShared()) {StringValue* newData = new StringValue(data->chars);data->removeReference();data = newData;}// 执行追加操作...size_t oldLen = strlen(data->chars);size_t appendLen = strlen(str);char* newChars = new char[oldLen + appendLen + 1];strcpy(newChars, data->chars);strcat(newChars, str);delete[] data->chars;data->chars = newChars;}private:struct StringValue {std::atomic<size_t> refCount;  // 原子引用计数char* chars;bool shareable;mutable std::mutex mutex;  // 用于线程安全StringValue(const char* initValue) : refCount(1), shareable(true) {chars = new char[strlen(initValue) + 1];strcpy(chars, initValue);}// 线程安全的引用操作void addReference() {std::lock_guard<std::mutex> lock(mutex);++refCount;}size_t removeReference() {std::lock_guard<std::mutex> lock(mutex);return --refCount;}bool isShared() const {std::lock_guard<std::mutex> lock(mutex);return refCount > 1;}void markUnshareable() {std::lock_guard<std::mutex> lock(mutex);shareable = false;}};StringValue* data;
};

2.2 通用引用计数模板

// 通用引用计数包装器
template<typename T>
class RCPtr {
public:RCPtr(T* realPtr = nullptr) : pointee(realPtr) {init();}RCPtr(const RCPtr& rhs) : pointee(rhs.pointee) {init();}~RCPtr() {if (pointee && pointee->removeReference() == 0) {delete pointee;}}RCPtr& operator=(const RCPtr& rhs) {if (pointee != rhs.pointee) {if (pointee && pointee->removeReference() == 0) {delete pointee;}pointee = rhs.pointee;init();}return *this;}T* operator->() const { return pointee; }T& operator*() const { return *pointee; }// 写时复制支持void makeCopy() {if (pointee->isShared()) {T* oldPtr = pointee;pointee = new T(*oldPtr);oldPtr->removeReference();pointee->addReference();}}private:void init() {if (pointee == nullptr) return;if (pointee->isShareable() == false) {pointee = new T(*pointee);}pointee->addReference();}T* pointee;
};// 可被引用计数管理的基类
class RCObject {
public:RCObject() : refCount(0), shareable(true) {}RCObject(const RCObject&) : refCount(0), shareable(true) {}virtual ~RCObject() = default;void addReference() { ++refCount; }size_t removeReference() { return --refCount; }size_t getRefCount() const { return refCount; }bool isShared() const { return refCount > 1; }bool isShareable() const { return shareable; }void markUnshareable() { shareable = false; }private:size_t refCount;bool shareable;
};// 使用示例:可引用计数的字符串
class RCString : public RCObject {
public:RCString(const char* value = "") {init(value);}RCString(const RCString& rhs) : RCObject(rhs) {init(rhs.data);}~RCString() {delete[] data;}const char* c_str() const { return data; }size_t length() const { return strlen(data); }// 修改操作char& operator[](size_t index) {// 需要在调用前调用makeCopy()return data[index];}// 只读访问const char& operator[](size_t index) const {return data[index];}private:void init(const char* value) {data = new char[strlen(value) + 1];strcpy(data, value);}char* data;
};

⚖️ 3. 解决方案与最佳实践

3.1 智能指针与引用计数结合

// 带引用计数的智能指针
template<typename T>
class RefCountedPtr {
public:explicit RefCountedPtr(T* ptr = nullptr) : pointer(ptr), refCount(new size_t(1)) {}RefCountedPtr(const RefCountedPtr& other) : pointer(other.pointer), refCount(other.refCount) {++(*refCount);}RefCountedPtr(RefCountedPtr&& other) noexcept : pointer(other.pointer), refCount(other.refCount) {other.pointer = nullptr;other.refCount = nullptr;}~RefCountedPtr() {decrementRefCount();}RefCountedPtr& operator=(const RefCountedPtr& other) {if (this != &other) {decrementRefCount();pointer = other.pointer;refCount = other.refCount;++(*refCount);}return *this;}RefCountedPtr& operator=(RefCountedPtr&& other) noexcept {if (this != &other) {decrementRefCount();pointer = other.pointer;refCount = other.refCount;other.pointer = nullptr;other.refCount = nullptr;}return *this;}T& operator*() const { return *pointer; }T* operator->() const { return pointer; }T* get() const { return pointer; }size_t use_count() const { return refCount ? *refCount : 0; }// 写时复制支持void make_unique() {if (use_count() > 1) {// 创建新副本T* newPointer = new T(*pointer);decrementRefCount();pointer = newPointer;refCount = new size_t(1);}}private:void decrementRefCount() {if (refCount) {--(*refCount);if (*refCount == 0) {delete pointer;delete refCount;}}}T* pointer;size_t* refCount;
};// 使用示例
class LargeData {
public:LargeData() { /* 昂贵的初始化 */ }LargeData(const LargeData&) { /* 昂贵的拷贝 */ }// ... 其他操作
};void usageExample() {RefCountedPtr<LargeData> data1(new LargeData());RefCountedPtr<LargeData> data2 = data1;  // 共享,不拷贝// 当需要修改时再创建副本data2.make_unique();  // 现在创建实际副本
}

3.2 线程安全的引用计数

// 线程安全的引用计数实现
template<typename T>
class ThreadSafeRefCounted {
public:ThreadSafeRefCounted() : refCount(1) {}virtual ~ThreadSafeRefCounted() = default;void addReference() {refCount.fetch_add(1, std::memory_order_relaxed);}size_t removeReference() {size_t oldCount = refCount.fetch_sub(1, std::memory_order_acq_rel);if (oldCount == 1) {delete this;}return oldCount - 1;}size_t getRefCount() const {return refCount.load(std::memory_order_relaxed);}protected:mutable std::atomic<size_t> refCount;
};// 线程安全的智能指针
template<typename T>
class TSRefCountedPtr {
public:explicit TSRefCountedPtr(T* ptr = nullptr) : pointer(ptr) {if (pointer) {pointer->addReference();}}TSRefCountedPtr(const TSRefCountedPtr& other) : pointer(other.pointer) {if (pointer) {pointer->addReference();}}TSRefCountedPtr(TSRefCountedPtr&& other) noexcept : pointer(other.pointer) {other.pointer = nullptr;}~TSRefCountedPtr() {if (pointer) {pointer->removeReference();}}TSRefCountedPtr& operator=(const TSRefCountedPtr& other) {if (this != &other) {// 先增加新对象的引用if (other.pointer) {other.pointer->addReference();}// 然后减少当前对象的引用if (pointer) {pointer->removeReference();}pointer = other.pointer;}return *this;}// 其他操作类似...private:T* pointer;
};

3.3 引用计数在复杂对象中的应用

// 复杂对象的引用计数管理
class Document : public ThreadSafeRefCounted {
public:static Document* create(const std::string& content) {return new Document(content);}// 只读访问const std::string& getContent() const {std::lock_guard<std::mutex> lock(mutex);return content;}// 修改操作需要线程安全void append(const std::string& text) {std::lock_guard<std::mutex> lock(mutex);content += text;++version;}size_t getVersion() const {std::lock_guard<std::mutex> lock(mutex);return version;}// 创建快照(写时复制)Document* createSnapshot() const {std::lock_guard<std::mutex> lock(mutex);return new Document(content, version);}private:Document(const std::string& content) : content(content), version(0) {}Document(const std::string& content, size_t ver): content(content), version(ver) {}std::string content;size_t version;mutable std::mutex mutex;
};// 使用智能指针管理文档
class DocumentHandle {
public:explicit DocumentHandle(Document* doc = nullptr) : document(doc) {}// 读取内容std::string read() const {if (document) {return document->getContent();}return "";}// 修改内容(自动处理引用计数)void write(const std::string& text) {if (document && document->getRefCount() > 1) {// 如果有其他引用,创建副本Document* newDoc = document->createSnapshot();document->removeReference();document = newDoc;}if (document) {document->append(text);}}// 创建新版本DocumentHandle createBranch() const {if (document) {Document* newDoc = document->createSnapshot();return DocumentHandle(newDoc);}return DocumentHandle();}private:Document* document;
};

💡 关键实践原则

  1. 识别共享场景
    当多个对象需要相同数据且读取远多于写入时,引用计数特别有效
  2. 写时复制优化
    延迟拷贝直到真正需要修改时,避免不必要的复制开销
  3. 线程安全考虑
    在多线程环境中使用原子操作确保引用计数安全
  4. 避免循环引用
    引用计数无法处理循环引用,需要配合弱引用或其他机制
  5. 性能权衡
    引用计数增加开销,适用于拷贝成本高而共享收益大的场景

引用计数 vs 垃圾收集

// 引用计数的优点:
// 1. 确定性销毁:资源立即释放
// 2. 预测性性能:没有GC暂停
// 3. 更少的内存开销:不需要GC数据结构// 引用计数的缺点:
// 1. 循环引用问题
// 2. 原子操作开销
// 3. 代码复杂性

现代C++中的引用计数

// std::shared_ptr 已经内置引用计数
void modernExample() {std::shared_ptr<LargeData> data1 = std::make_shared<LargeData>();std::shared_ptr<LargeData> data2 = data1;  // 共享引用// 如果需要修改且避免影响其他使用者if (!data2.unique()) {data2 = std::make_shared<LargeData>(*data2);  // 深拷贝}// 现在可以安全修改data2
}

总结
引用计数是一种强大的资源管理技术,通过在多个使用者间共享资源而非复制,显著提高内存效率和性能。

关键实现技术包括写时复制、原子操作确保线程安全、以及适当的抽象来隐藏复杂性。现代C++中std::shared_ptr提供了内置的引用计数,但在特定场景下自定义实现可能更有优势。

正确使用引用计数可以大幅减少不必要的资源拷贝,同时保持代码的清晰性和资源管理的可靠性。但在使用时需要注意循环引用问题和性能权衡。


文章转载自:

http://jG2jbNHL.jmbfx.cn
http://Q2NjKk10.jmbfx.cn
http://9fXd6BrY.jmbfx.cn
http://EFjmUpm8.jmbfx.cn
http://zlWs5hiO.jmbfx.cn
http://SVu5YooT.jmbfx.cn
http://gB2KHtbg.jmbfx.cn
http://8hYKiQqL.jmbfx.cn
http://6c5bR3oi.jmbfx.cn
http://4J6Klr1Q.jmbfx.cn
http://xSiVOCyE.jmbfx.cn
http://hGlIsWhP.jmbfx.cn
http://8gQ3kJe2.jmbfx.cn
http://sbhxTFJa.jmbfx.cn
http://LRVUWFMX.jmbfx.cn
http://QY0OB1p3.jmbfx.cn
http://d6jJKEVn.jmbfx.cn
http://fpSgXKSU.jmbfx.cn
http://wrXJMo3L.jmbfx.cn
http://gLwtoZsS.jmbfx.cn
http://Lfxk6Kv0.jmbfx.cn
http://149uIOBX.jmbfx.cn
http://gppLRAGV.jmbfx.cn
http://lA1M06K7.jmbfx.cn
http://BQneucQ0.jmbfx.cn
http://6QVGU2Wk.jmbfx.cn
http://LXgp4meI.jmbfx.cn
http://8K5ZcnQI.jmbfx.cn
http://8tB47wgU.jmbfx.cn
http://Cr2PTQJi.jmbfx.cn
http://www.dtcms.com/a/368223.html

相关文章:

  • 人形机器人控制系统核心芯片从SoC到ASIC的进化路径
  • Docker学习笔记(三):镜像与容器管理进阶操作
  • excel里面店铺这一列的数据结构是2C【uniteasone17】这种,我想只保留前面的2C部分,后面的【uniteasone17】不要
  • Qt图片资源导入
  • 苍穹外卖Day10 | 订单状态定时处理、来单提醒、客户催单、SpringTask、WebSocket、cron表达式
  • 01-Hadoop简介与生态系统
  • 如何利用静态代理IP优化爬虫策略?从基础到实战的完整指南
  • 信息安全工程师考点-网络信息安全概述
  • 功能强大的多线程端口扫描工具,支持批量 IP 扫描、多种端口格式输入、扫描结果美化导出,适用于网络安全检测与端口监控场景
  • 自定义格式化数据(BYOFD)(81)
  • 人工智能时代职能科室降本增效KPI设定全流程与思路考察
  • 使用 chromedp 高效爬取 Bing 搜索结果
  • Linux 命令速查宝典:从入门到高效操作
  • 【科研绘图系列】R语言绘制论文合集图
  • 分类、目标检测、实例分割的评估指标
  • 卷积神经网络进行图像分类
  • Java JVM核心原理与面试题解析
  • 【Flutter】RefreshIndicator 无法下拉刷新问题
  • 基于Django+Vue3+YOLO的智能气象检测系统
  • Flutter的三棵树
  • React 样式隔离核心方法和最佳实践
  • 踩坑实录:Django继承AbstractUser时遇到的related_name冲突及解决方案
  • 【Flutter】flutter_local_notifications并发下载任务通知实践
  • 覆盖Transformer、GAN:掩码重建正在重塑时间序列领域!
  • 数据结构基础之队列:数组/链表
  • 数据可视化工具推荐:5款让图表制作轻松上手的神器
  • 【网安基础】--ip地址与子网掩码
  • spring AI 的简单使用
  • 【yolo】YOLOv8 训练模型参数与多机环境差异总结
  • 算法(keep learning)