深入理解C++智能指针 - 新手必读指南

深入理解C++智能指针 - 新手必读指南

编码文章call10242025-05-08 11:55:444A+A-

目录

  • 1. 为什么要用智能指针?
  • 2. unique_ptr详解
  • 3. shared_ptr深度解析
  • 4. weak_ptr的妙用
  • 5. 新手常见错误大全
  • 6. 智能指针使用黄金法则

1. 为什么要用智能指针?

现实场景:

假设你正在开发一个文档编辑器,需要频繁创建/销毁文本缓冲区:

void loadDocument() {
    TextBuffer* buf = new TextBuffer();
    // 如果这里出现异常...
    delete buf; // 可能永远不会执行!
}

智能指针可以保证无论是否发生异常,内存都会被正确释放

2. unique_ptr详解

工作原理:

就像你的个人保险箱:

  • 只有一把钥匙(所有权)
  • 钥匙可以转交他人(移动语义)
  • 离开作用域自动销毁

完整使用示例:

#include <memory>
#include <vector>

// 创建独占指针
std::unique_ptr<std::vector<int>> createVector() {
    auto ptr = std::make_unique<std::vector<int>>();
    ptr->push_back(42);
    return ptr; // 通过移动操作返回
}

int main() {
    std::unique_ptr<std::vector<int>> data;
    {
        auto temp = createVector();
        data = std::move(temp); // 所有权转移
        // temp现在为空指针
    }
    std::cout << data->at(0); // 安全访问
    return 0;
}

典型错误:

auto ptr1 = std::make_unique<int>(10);
auto ptr2 = ptr1; // 错误!尝试复制

// 正确做法:
auto ptr2 = std::move(ptr1); // 显式转移所有权

编译器会直接报错,避免潜在的内存问题

3. shared_ptr深度解析

核心机制:

想象办公室的公共打印机:

  • 每个使用者登记(引用计数+1)
  • 最后一个离开的人关打印机(计数归零时释放)
  • 控制块存储计数信息

完整生命周期示例:

class NetworkConnection {
public:
    NetworkConnection() { std::cout << "连接建立\n"; }
    ~NetworkConnection() { std::cout << "连接关闭\n"; }
};

void processData() {
    auto conn = std::make_shared<NetworkConnection>();
    // 引用计数:1
    
    {
        auto conn2 = conn; // 复制指针
        // 引用计数:2
        conn->send(data);
    } // conn2析构,计数回到1
    
    conn.reset(); // 手动释放,计数归零
    // 输出"连接关闭"
}

循环引用陷阱:

class ChatUser {
public:
    std::shared_ptr<ChatUser> friend;
};

auto alice = std::make_shared<ChatUser>();
auto bob = std::make_shared<ChatUser>();

alice->friend = bob;
bob->friend = alice; // 循环引用!

即使alice和bob离开作用域,引用计数也不会归零

4. weak_ptr的妙用

使用场景:

就像查看餐厅排队情况:

  • 可以查看是否有空位(lock()检查)
  • 不会影响餐厅关闭时间(不增加引用计数)

完整缓存示例:

class ResourceCache {
    std::weak_ptr<Texture> cachedTexture;
    
public:
    std::shared_ptr<Texture> getTexture() {
        if(auto tex = cachedTexture.lock()) {
            return tex; // 缓存命中
        } else {
            auto newTex = loadTexture();
            cachedTexture = newTex;
            return newTex; // 重新加载
        }
    }
};

void renderScene() {
    auto cache = std::make_shared<ResourceCache>();
    auto texture = cache->getTexture();
    // 使用texture...
} // texture释放时,缓存自动失效

5. 新手常见错误大全

错误1:跨DLL内存管理

// 在DLL A中创建
std::shared_ptr<Data> createData() {
    return std::make_shared<Data>();
}

// 在DLL B中释放
void processData() {
    auto data = createData();
} // 可能导致崩溃!

解决方案:确保分配/释放在同一模块

错误2:错误使用原始指针

auto sp = std::make_shared<int>(10);
int* raw = sp.get();
{
    auto sp2 = std::shared_ptr<int>(raw);
} // 此处释放内存!
*sp = 20; // 悬空指针!

6. 智能指针使用黄金法则

场景

推荐方案

示例

独占资源

unique_ptr

文件句柄、数据库连接

共享资源

shared_ptr

缓存数据、UI组件

观察资源

weak_ptr

缓存系统、解耦模块

性能优化建议:

  • 优先使用make_shared/make_unique
  • 避免大对象的shared_ptr拷贝
  • 高频访问数据使用unique_ptr
点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4