C++标准库:智能指针

      在C++中,智能指针是用于管理动态分配对象生命周期的工具,它们通过自动化的内存管理来避免内存泄漏、悬空指针和其他与手动管理内存相关的常见错误。C++11引入了三种主要类型的智能指针:std::unique_ptr、std::shared_ptr和std::weak_ptr,它们都定义在头文件<memory>中。

智能指针介绍

1. std::unique_ptr

  • 独占所有权std::unique_ptr表示对其所指向的对象具有独占所有权的指针。当std::unique_ptr被销毁时,它所管理的对象也会被自动销毁。
  • 不可复制:为了保持独占性,std::unique_ptr不能被复制,但可以通过std::move转移所有权。
  • 适用场景:当你确定资源应该只有一个所有者时使用(如工厂模式、资源句柄)。

示例代码:

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> ptr = std::make_unique<int>(10);
    // 使用 *ptr 访问值
    std::cout << "*ptr: " << *ptr << std::endl;

    // 通过 std::move 转移所有权
    auto ptr2 = std::move(ptr);
    if (!ptr) {
        std::cout << "ptr is now null." << std::endl;
    }
}

2. std::shared_ptr

  • 共享所有权std::shared_ptr允许多个指针共享同一个对象的所有权。引用计数用于跟踪有多少std::shared_ptr实例指向同一对象;当最后一个std::shared_ptr被销毁时,该对象也会被销毁。
  • 线程安全:引用计数的操作是线程安全的。
  • 适用场景:当需要多个指针共享同一资源时使用。

示例代码:

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> ptr = std::make_shared<int>(20);
    {
        auto ptr2 = ptr; // 增加引用计数
        std::cout << "ptr.use_count(): " << ptr.use_count() << std::endl; // 输出: 2
    } // ptr2 被销毁,减少引用计数

    std::cout << "ptr.use_count(): " << ptr.use_count() << std::endl; // 输出: 1
}

3. std::weak_ptr

  • 弱引用std::weak_ptr提供了一种不增加引用计数的方式来指向一个由std::shared_ptr管理的对象。它可以用来打破循环引用,并且不会影响对象的生命周期。
  • 观察角色std::weak_ptr通常用于观察某个对象而不影响其生命周期。
  • 转换为std::shared_ptr:要访问由std::weak_ptr指向的对象,必须先将其转换为std::shared_ptr

示例代码:

class B; // 前向声明

class A {
public:
    std::shared_ptr<B> b_ptr;
    ~A() { std::cout << "A destroyed\n"; }
};

class B {
public:
    std::weak_ptr<A> a_weak_ptr; // 使用 weak_ptr 打破循环引用
    ~B() { std::cout << "B destroyed\n"; }
};

int main() {
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();
    a->b_ptr = b;
    b->a_weak_ptr = a; // 不会增加引用计数
    // a 和 b 能正确析构
}

注意事项

  1. 避免裸指针混用

    int* raw_ptr = new int(42);
    std::shared_ptr<int> p1(raw_ptr);
    std::shared_ptr<int> p2(raw_ptr); // 错误:双重释放!
  2. 优先使用 make_shared 和 make_unique

    • 更高效(减少内存分配次数)。

    • 避免因异常导致的内存泄漏。

  3. 循环引用问题

    • 使用 weak_ptr 打破 shared_ptr 的循环引用。

  4. 性能开销

    • shared_ptr 的引用计数需原子操作,可能影响性能(高频场景需谨慎)。

  5. 数组管理

    • C++17 前:需自定义删除器(如 shared_ptr<int[]> 在 C++17 支持)。

    • C++11/14:

      std::unique_ptr<int[]> arr(new int[5]); // 支持数组
      std::shared_ptr<int> arr2(new int[5], std::default_delete<int[]>()); // 需自定义删除器

       6. 避免返回 shared_ptr 的裸指针

std::shared_ptr<int> p = std::make_shared<int>(10);
int* raw = p.get();
// 若 p 被释放,raw 成为悬空指针!

 

动态内存管理

       在C++11之前,动态内存管理主要依赖于newdelete操作符来分配和释放堆上的内存。由于缺乏智能指针的帮助,开发者需要手动管理对象的生命周期,这增加了程序出现内存泄漏、悬挂指针等问题的风险。以下是C++11之前常用的几种内存管理技术:

1. 手动使用 new 和 delete

  • 分配内存:使用new关键字为对象分配内存,并返回指向该对象的指针。
  • 释放内存:使用delete关键字释放由new分配的内存,以避免内存泄漏。
#include <iostream>

class MyClass {
public:
    MyClass() { std::cout << "MyClass constructor called" << std::endl; }
    ~MyClass() { std::cout << "MyClass destructor called" << std::endl; }
};

int main() {
    MyClass* myObj = new MyClass(); // 分配内存
    // 使用 myObj...
    delete myObj; // 释放内存

    return 0;
}

2. 自定义内存管理类

        为了更好地管理资源并确保在离开作用域时正确释放资源,开发人员可能会编写自定义的类或函数来封装资源管理逻辑。一个常见的模式是使用构造函数获取资源,析构函数释放资源,这种方法被称为RAII(Resource Acquisition Is Initialization)。

示例代码:

#include <iostream>

class ResourceHandler {
private:
    int* data;
public:
    ResourceHandler(int size) : data(new int[size]) {}
    ~ResourceHandler() { delete[] data; }

    // 禁止复制构造函数和赋值操作符以防止浅拷贝
    ResourceHandler(const ResourceHandler&) = delete;
    ResourceHandler& operator=(const ResourceHandler&) = delete;
};

int main() {
    ResourceHandler handler(10); // 资源在构造时被获取,在析构时被释放

    return 0;
}

3. 工具类如 std::auto_ptr

        虽然不是C++11的一部分,但在C++98/03标准中引入了std::auto_ptr,它是一个早期尝试提供类似现代智能指针功能的工具。然而,由于其所有权转移机制存在问题(例如不能安全地用于STL容器),它在C++11中已被弃用,并最终在C++17中被移除。

示例代码:

#include <memory>
#include <iostream>

class MyClass {
public:
    MyClass() { std::cout << "MyClass constructor called" << std::endl; }
    ~MyClass() { std::cout << "MyClass destructor called" << std::endl; }
};

int main() {
    std::auto_ptr<MyClass> myAutoPtr(new MyClass()); // 使用 auto_ptr 管理 MyClass 实例

    return 0;
}

尽管std::auto_ptr提供了一定程度的自动化内存管理,但它的设计缺陷使其不适合所有场景,特别是当涉及到多个指针共享同一资源时。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值