4-C++智能指针

为什么需要智能指针-WHY

C++通常使用new和delete进行动态内存管理(C中使用malloc和free)。但是,现代的C++鼓励使用智能指针和容器来管理动态内存,以避免内存泄漏和其他问题。

智能指针是资源获取即初始化(RAII)思想的典型应用。C++中的智能指针是管理动态分配内存的工具,它们可以自动释放所管理的内存,防止内存泄漏定义在<memory>头文件中。

内存泄漏是指程序在动态分配内存后,由于程序逻辑错误或设计缺陷,失去了对该内存区域的有效引用,从而无法释放已分配的内存空间,导致系统内存资源被无效占用且无法回收的现象。

特性 unique_ptr shared_ptr weak_ptr
所有权 独占 共享 无所有权
拷贝语义 不可拷贝,只能移动 可拷贝 可拷贝
性能 零开销 有引用计数开销 有开销
使用场景 单一所有者 多个所有者 解决循环引用
线程安全 引用计数操作原子性

什么是智能指针-WHAT

1. std::unique_ptr - 独占所有权指针

std::make_unique<T> 返回一个 std::unique_ptr<T> 对象

特点:

  • 独占所有权 :同一时间只能有一个 unique_ptr 拥有对象
  • 不可拷贝,只能移动(move)
  • 零开销:与裸指针几乎相同的性能
  • 自动释放:离开作用域时自动删除对象

使用场景

  • 资源管理(数据库连接,文件操作等)
  • 工程模式下对象创建
cpp 复制代码
#include <memory>

// unique_ptr的创建方法
std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
auto ptr2 = std::make_unique<std::string>("Hello");

// 移动语义
std::unique_ptr<int> ptr3 = std::move(ptr1); // ptr1 变为 nullptr

// 自定义删除器
std::unique_ptr<FILE, decltype(&fclose)> filePtr(fopen("test.txt", "r"), &fclose);
cpp 复制代码
// 定义支付处理类
class PaymentProcessor {
public:
    virtual ~PaymentProcessor() = default; //析构函数
    virtual bool process(double amount) = 0; // 虚函数
};
// 定义信用卡支付处理类
class CreditCardProcessor : public PaymentProcessor {
public:
    bool process(double amount) override {
        std::cout << "处理信用卡支付: $" << amount << std::endl;
        return true;
    }
};
// 定义PayPal支付处理类
class PayPalProcessor : public PaymentProcessor {
public:
    bool process(double amount) override {
        std::cout << "处理PayPal支付: $" << amount << std::endl;
        return true;
    }
};
// 支付类创建(工厂模式)
class PaymentFactory {
public:
    static std::unique_ptr<PaymentProcessor> createProcessor(const std::string& type) {
        if (type == "creditcard") {
            return std::make_unique<CreditCardProcessor>();
        } else if (type == "paypal") {
            return std::make_unique<PayPalProcessor>();
        }
        return nullptr;
    }
};

// 使用工厂
void processPayment(const std::string& method, double amount) {
    auto processor = PaymentFactory::createProcessor(method);
    if (processor) {
        processor->process(amount);
    }
    // 函数执行完后,processor 自动释放(自动调用析构函数),无需手动删除
}

2. std::shared_ptr - 共享所有权指针

每个 shared_ptr 管理的对象都有一个关联的控制块(Control Block)

控制块中包含:

  • 引用计数:记录当前有多少个 shared_ptr 共享该对象

  • 弱引用计数:记录 weak_ptr 的数量

  • 被管理对象的指针

  • 删除器(可选)

    shared_ptr A ──────┐

    shared_ptr B ──────┤ Control Block
    │ ┌─────────────┐
    shared_ptr C ──────┼───►│ ref count: 3│
    │ │ weak count: 1│
    weak_ptr D ──────┤ │ object ptr ─┼───► Actual Object
    │ │ deleter │
    │ └─────────────┘

特点:

  • 共享所有权 :多个 shared_ptr 可以共享同一个对象
  • 引用计数:内部维护引用计数,计数为0时自动删除对象(创建和拷贝时计数增加)
  • 可以拷贝
  • 有一定开销:需要维护引用计数

使用场景:

cpp 复制代码
#include <memory>


std::shared_ptr<int> shared1 = std::make_shared<int>(100); // 创建 shared_ptr
std::shared_ptr<int> shared2 = shared1; // 引用计数+1

std::cout << "引用计数: " << shared1.use_count() << std::endl; // 输出 2

// 自定义删除器
std::shared_ptr<int> shared3(new int[10], [](int* p) { delete[] p; });
cpp 复制代码
class Configuration {
    // ...
};

class Component {
private:
    std::shared_ptr<Configuration> config_;
public:
    Component(std::shared_ptr<Configuration> config) : config_(config) {}
    // ...
};

// 创建配置对象
auto config = std::make_shared<Configuration>();

// 创建多个组件共享配置
Component comp1(config);
Component comp2(config);

// 当comp1, comp2都被销毁,且config引用计数为0时,Configuration对象自动释放

3. std::weak_ptr - 弱引用指针

特点:

  • 不增加引用计数 :观察 shared_ptr 但不拥有所有权
  • 解决循环引用问题
cpp 复制代码
// QT 页面调用
class PageA {
    std::shared_ptr<PageB> b_ptr;
};

class PageB {
    std::shared_ptr<PageA> a_ptr;  // 循环引用!
    //std::weak_ptr<PageA> a_weak_ptr; // 使用weak_ptr打破循环引用
};
  • 需要通过 lock() 方法获取可用的 shared_ptr

使用场景:

cpp 复制代码
std::shared_ptr<int> shared = std::make_shared<int>(50);
std::weak_ptr<int> weak = shared;

// 使用 weak_ptr
if (auto temp = weak.lock()) {
    std::cout << "值: " << *temp << std::endl;
} else {
    std::cout << "对象已被销毁" << std::endl;
}

实际应用示例

cpp 复制代码
class DatabaseConnection {
public:
    static std::unique_ptr<DatabaseConnection> create(const std::string& connStr) {
        return std::make_unique<DatabaseConnection>(connStr);
    }
        void execute(const std::string& query) {
        // 执行SQL
    }
    ~DatabaseConnection() {
        // 自动关闭连接
        std::cout << "数据库连接已关闭\n";
    }
};

// RAII方式(自动管理)
void processUserData() {
    auto db = DatabaseConnection::create("connStr"); // 自动管理
    db->execute("UPDATE users...");
    // 不用担心,连接会自动关闭
}
// 传统方式
void processUserData() {
    DatabaseConnection* db = new DatabaseConnection("connStr");
    db->execute("UPDATE users...");
    // 如果这里return或抛出异常,连接永远不会关闭!
    delete db; // 需要手动释放
}
cpp 复制代码
class ImageCache {
private:
    std::unordered_map<std::string, std::shared_ptr<Image>> cache_;
    std::mutex mutex_;
    
public:
    std::shared_ptr<Image> getImage(const std::string& path) {
        std::lock_guard<std::mutex> lock(mutex_);
        
        auto it = cache_.find(path);
        if (it != cache_.end()) {
            return it->second; // 返回共享的图像
        }
        
        // 加载新图像
        auto image = std::make_shared<Image>(path);
        cache_[path] = image;
        return image;
    }
};

// 多个UI组件共享同一图像资源
class UIComponent {
    std::shared_ptr<Image> background_;
public:
    UIComponent(const std::shared_ptr<Image>& bg) : background_(bg) {}
    
    void render() {
        // 使用共享的背景图像
        // 当所有UI组件都销毁时,图像自动从缓存中释放
    }
};

4. std::auto_ptr (已废弃)

注意: C++17 中已移除,不推荐使用。

相关推荐
liu****3 小时前
10.queue的模拟实现
开发语言·数据结构·c++·算法
宋恩淇要努力3 小时前
C++多态
c++
哦你看看3 小时前
学习Python 03
开发语言·windows·python
小龙报4 小时前
《彻底理解C语言指针全攻略(6)-- qsort、sizeof和strlen》
c语言·开发语言·职场和发展·创业创新·学习方法·业界资讯·visual studio
让我们一起加油好吗4 小时前
【基础算法】01BFS
数据结构·c++·算法·bfs·01bfs
郝学胜-神的一滴4 小时前
Three.js光照技术详解:为3D场景注入灵魂
开发语言·前端·javascript·3d·web3·webgl
fie88894 小时前
基于Matlab的深度堆叠自编码器(SAE)实现与分类应用
开发语言·分类
_w_z_j_4 小时前
C++11----列表初始化和initializer_list
开发语言·c++
冬天的雪20084 小时前
java内存性能优化工具Mat
java·开发语言