【C++】std::shared_ptr智能指针详解和示例

在C++中,智能指针是一种用于自动管理动态分配内存的机制,旨在减少内存泄漏和野指针的风险。std::shared_ptr 是C++标准库提供的几种智能指针之一,它通过共享所有权的机制来管理动态分配的对象。本文将详细解析 std::shared_ptr 的工作原理、特性,并通过一个应用案例来展示其在实际开发中的使用。

std::shared_ptr 工作原理

std::shared_ptr 使用一个内部计数器(通常称为控制块)来跟踪有多少个 std::shared_ptr 实例共享同一个对象。每当一个新的 std::shared_ptr 被创建并指向某个对象时,计数器就会递增;当 std::shared_ptr 被销毁或重置时,计数器就会递减。当计数器减至零时,表明没有 std::shared_ptr 实例再共享该对象,此时对象将被自动删除,释放其所占用的内存。

主要特性

自动内存管理:当 std::shared_ptr 的实例被销毁时(例如,离开作用域时),它会检查其所指向的对象是否还有其他 std::shared_ptr 实例在共享所有权。如果没有其他实例,则自动删除该对象。
共享所有权:多个 std::shared_ptr 实例可以指向同一个对象,每个实例都拥有该对象的一部分所有权。当所有权计数变为零时,对象被删除。
线程安全:在增加或减少所有权计数时,std::shared_ptr 提供了必要的同步机制,以确保操作的原子性。但是,这并不意味着对共享对象的操作本身是线程安全的。
自定义删除器:可以指定一个自定义的删除器,以便在删除对象时执行特定的清理操作。

成员函数

get():返回原始指针。
reset():重置 std::shared_ptr,可以选择指向一个新对象或变为空。
use_count():返回共享此对象的 std::shared_ptr 实例的数量(线程安全)。
unique():如果 use_count() 返回 1,则返回 true,表示当前 std::shared_ptr 是唯一指向其对象的实例。
swap():交换两个 std::shared_ptr 实例的内容。

注意事项

当使用 reset() 方法将 std::shared_ptr 重置为另一个对象或 nullptr 时,如果原对象没有其他 std::shared_ptr 实例在共享所有权,则原对象将被删除。
如果 std::shared_ptr 持有的是指向动态分配数组的指针,则应该使用 std::shared_ptr<T[]>(C++11 引入的数组特化)或自定义删除器来确保数组被正确删除。
循环引用是 std::shared_ptr 的一个潜在问题,它会导致内存泄漏。循环引用发生在两个或多个 std::shared_ptr 实例相互引用,从而阻止对方被销毁。为了解决这个问题,可以使用 std::weak_ptr。

应用案例

示例1

假设我们正在开发一个图形库,其中包含了多种图形元素(如圆形、矩形等),这些图形元素需要被动态创建并在需要时被自动销毁。我们可以使用 std::shared_ptr 来管理这些图形元素的内存。

以下是一个简化的应用案例:

#include <iostream>
#include <memory>
#include <vector>

class Shape {
public:
    virtual void draw() const = 0;
   
};

class Circle : public Shape {
    int radius;
public:
    Circle(int r) : radius(r) {}
    void draw() const override {
        std::cout << "Drawing a circle with radius " << radius << std::endl;
    }
};

class Rectangle : public Shape {
    int width, height;
public:
    Rectangle(int w, int h) : width(w), height(h) {}
    void draw() const override {
        std::cout << "Drawing a rectangle with width " << width << " and height " << height << std::endl;
    }
};

int main() {
    std::vector<std::shared_ptr<Shape>> shapes;

    // 动态创建图形元素并添加到shapes中
    shapes.push_back(std::make_shared<Circle>(5));
    shapes.push_back(std::make_shared<Rectangle>(10, 20));

    // 遍历并绘制所有图形
    for (const auto& shape : shapes) {
        shape->draw();
    }

    // 当shapes的作用域结束时,所有图形元素将被自动销毁

    return 0;
}

在这个例子中,我们定义了一个 Shape 基类和两个派生类 Circle 和 Rectangle。我们使用 std::vector<std::shared_ptr> 来存储不同类型的图形元素,并利用 std::make_shared 来创建 std::shared_ptr 实例。这样,我们就能够自动管理这些图形元素的内存,而无需担心内存泄漏或野指针的问题。

在这里插入图片描述

示例2

#include <iostream>
#include <memory>
#include <vector>

class Node {
public:
    int value;
    std::vector<std::shared_ptr<Node>> neighbors;

    Node(int val) : value(val) {}

    void addNeighbor(std::shared_ptr<Node> neighbor) {
        neighbors.push_back(neighbor);
    }

    // 打印节点的值及其邻居的值
    void printNeighbors() const {
        std::cout << "Node " << value << " neighbors: ";
        for (const auto& neighbor : neighbors) {
            std::cout << neighbor->value << " ";
        }
        std::cout << std::endl;
    }
};

int main() {
    // 创建节点
    std::shared_ptr<Node> node1 = std::make_shared<Node>(1);
    std::shared_ptr<Node> node2 = std::make_shared<Node>(2);
    std::shared_ptr<Node> node3 = std::make_shared<Node>(3);

    // 添加邻居
    node1->addNeighbor(node2);
    node1->addNeighbor(node3);
    node2->addNeighbor(node1);
    node3->addNeighbor(node1);

    // 打印结果
    node1->printNeighbors(); // 输出 Node 1 的邻居
    node2->printNeighbors(); // 输出 Node 2 的邻居
    node3->printNeighbors(); // 输出 Node 3 的邻居

    // 注意:这里没有循环引用问题,因为每个节点都持有其邻居的弱引用(实际上这里是强引用,但在更复杂的图中可能会使用弱引用来避免循环引用)

    // 当这些 shared_ptr 离开作用域时,它们指向的 Node 对象将被自动删除
    return 0;
}

在这里插入图片描述

结论

std::shared_ptr 是C++标准库中一个强大且灵活的工具,它通过共享所有权的机制来自动管理动态分配的内存。在实际开发中,我们可以利用 std::shared_ptr 来简化内存管理,减少内存泄漏的风险。然而,我们也需要注意循环引用的问题,并在必要时使用 std::weak_ptr 来打破循环引用。

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要将C++std::shared_ptr<char>写入文件,可以使用std::ofstream类来打开文件并写入数据。首先,你需要创建一个std::shared_ptr<char>,并将其初始化为指向要写入的字符数组。然后,你可以使用std::ofstream的写入函数将数据写入文件。最后,确保在程序结束时关闭文件和释放内存。 以下是一个示例代码,演示了如何将std::shared_ptr<char>写入文件: ```cpp #include <iostream> #include <fstream> #include <memory> int main() { // 创建一个包含要写入的数据的shared_ptr<char> std::shared_ptr<char> data(new char{'H', 'e', 'l', 'l', 'o'}); // 打开文件并写入数据 std::ofstream file("output.txt", std::ios::binary); if (file.is_open()) { file.write(data.get(), 5); file.close(); std::cout << "数据已成功写入文件" << std::endl; } else { std::cout << "无法打开文件" << std::endl; } // 注意:不需要手动释放内存,shared_ptr会在不再使用时自动释放 return 0; } ``` 上面的代码将字符数组"Hell0"写入了名为"output.txt"的文件中。注意,我们使用了`std::ios::binary`来打开文件,这是因为我们要写入二进制数据。如果要写入文本数据,可以省略这个参数。 请确保你在使用代码之前包含了相关的头文件,并根据自己的需求修改文件名和要写入的数据。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C++智能指针2——共享指针shared_ptr详解](https://blog.csdn.net/xuyouqiang1987/article/details/104034184)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木彳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值