共享智能指针 (std::shared_ptr)

在 C++ 中,智能指针(Smart Pointers)是用于管理动态分配内存的工具,可以自动释放内存,避免内存泄漏。它们在 C++11 及后续标准中通过标准库 提供,主要包括以下三种类型:
C++11中提供了三种智能指针,使用这些智能指针时需要引用头文件

  • std::shared_ptr: 共享智能指针
  • std::unique_ptr: 独占智能指针
  • std::weak_ptr: 弱引用的智能指针,它不共享指针,不能操作资源,是用来监视shared_ptr的。

1.shared_ptr的初始化

在 C++ 中,std::shared_ptr 的初始化有多种方式,推荐使用安全且高效的方法。以下是 std::shared_ptr 的常见初始化方式及注意事项:

1.使用 std::make_shared初始化(推荐)

  • 方式: 通过 std::make_shared 创建 std::shared_ptr,这是 C++11 引入的首选方法
  • 优点:
    • 更高效:一次性分配对象和引用计数控制块的内存。
    • 更安全:避免了直接使用 new 可能导致的异常问题。
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <memory>
    #include <iostream>

    struct MyClass {
    int value;
    MyClass(int v) : value(v) {}
    ~MyClass() { std::cout << "Destroyed\n"; }
    };

    int main() {
    std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>(42);
    std::cout << ptr->value << "\n"; // 输出 42
    return 0; // 自动释放
    }
  • 适用场景:大多数情况下创建 shared_ptr 的首选方式。

2.通过构造函数new初始化

  • 方式: 直接将 new 分配的指针传入 std::shared_ptr 构造函数。
  • 缺点:
    • 性能稍差:new 和控制块分配是分开的。
    • 不安全:如果在构造 shared_ptr 前抛出异常,可能导致内存泄漏。
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <memory>
    #include <iostream>

    struct MyClass {
    ~MyClass() { std::cout << "Destroyed\n"; }
    };

    int main() {
    std::shared_ptr<MyClass> ptr(new MyClass());
    return 0; // 自动释放
    }
  • 注意:尽量避免此方式,优先使用 std::make_shared

3.通过另一个 shared_ptr 初始化(共享所有权)

  • 方式:通过拷贝构造或赋值操作,多个 shared_ptr 共享同一资源,引用计数增加。
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <memory>
    #include <iostream>

    struct MyClass {
    ~MyClass() { std::cout << "Destroyed\n"; }
    };

    int main() {
    std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
    std::shared_ptr<MyClass> ptr2 = ptr1; // 拷贝,引用计数增至 2
    std::cout << "Ref count: " << ptr1.use_count() << "\n"; // 输出 2
    return 0; // 资源在最后一个 shared_ptr 销毁时释放
    }

4.通过 std::unique_ptr 转移所有权

  • 方式:将 std::unique_ptr 的所有权转移给 std::shared_ptr。
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <memory>
    #include <iostream>

    struct MyClass {
    ~MyClass() { std::cout << "Destroyed\n"; }
    };

    int main() {
    std::unique_ptr<MyClass> unique = std::make_unique<MyClass>();
    std::shared_ptr<MyClass> shared = std::move(unique); // 转移所有权
    std::cout << (unique == nullptr) << "\n"; // 输出 1(unique 已为空)
    return 0;
    }
  • 注意:转移后,unique_ptr 变为空。

2.指定删除器

在 C++ 中,std::shared_ptr 支持指定自定义删除器(deleter),以在资源释放时执行特定清理操作。自定义删除器在管理非标准内存资源(如文件句柄、自定义分配的内存等)或需要特殊清理逻辑时非常有用。以下是关于 std::shared_ptr 指定删除器的详细说明和示例。

1.使用函数指针作为删除器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <memory>
#include <iostream>

struct MyClass {
~MyClass() { std::cout << "MyClass destroyed\n"; }
};

// 自定义删除器函数
void customDeleter(MyClass* ptr) {
std::cout << "Custom deleter called\n";
delete ptr;
}

int main() {
std::shared_ptr<MyClass> ptr(new MyClass(), customDeleter);
return 0; // 资源释放时调用 customDeleter
}
  • 输出:
    1
    2
    MyClass destroyed
    Custom deleter called
  • 说明:删除器 customDeletershared_ptr 销毁时被调用,执行自定义清理逻辑。

2.使用 lambda 表达式作为删除器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <memory>
#include <iostream>

struct MyClass {
~MyClass() { std::cout << "MyClass destroyed\n"; }
};

int main() {
std::shared_ptr<MyClass> ptr(new MyClass(), [](MyClass* ptr) {
std::cout << "Lambda deleter called\n";
delete ptr;
});
return 0;
}
  • 输出:
    1
    2
    MyClass destroyed
    Lambda deleter called
  • 优点:Lambda 表达式更灵活,适合临时定义删除逻辑。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include <memory>
    #include <functional>
    #include <iostream>

    struct MyClass {
    ~MyClass() { std::cout << "MyClass destroyed\n"; }
    };

    int main() {
    std::function<void(MyClass*)> deleter = [](MyClass* ptr) {
    std::cout << "std::function deleter called\n";
    delete ptr;
    };
    std::shared_ptr<MyClass> ptr(new MyClass(), deleter);
    return 0;
    }
  • 输出:
    1
    2
    MyClass destroyed
    std::function deleter called
  • 说明:std::function 提供更大的灵活性,但有额外开销,适合复杂场景。