首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——16.智能指针

移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——16.智能指针

作者头像
hope kc
发布2025-07-29 08:33:26
发布2025-07-29 08:33:26
17100
代码可运行
举报
文章被收录于专栏:学习学习
运行总次数:0
代码可运行

C++智能指针使用格式与核心函数详解

智能指针是C++中管理动态内存的强大工具,它们通过自动化的内存管理机制大大减少了内存泄漏和悬空指针的风险。下面我将详细解释各种智能指针的使用格式及其核心成员函数。

一、智能指针的基本使用格式

1. std::unique_ptr

创建格式:

代码语言:javascript
代码运行次数:0
运行
复制
// 方式1:直接构造
std::unique_ptr<类型> ptr(new 类型(构造参数));

// 方式2:使用std::make_unique (C++14起)
auto ptr = std::make_unique<类型>(构造参数);

示例:

代码语言:javascript
代码运行次数:0
运行
复制
// 创建管理int的unique_ptr
std::unique_ptr<int> numPtr(new int(42));

// 创建管理自定义类的unique_ptr
class MyClass { /*...*/ };
auto objPtr = std::make_unique<MyClass>(constructorArgs);
2. std::shared_ptr

创建格式:

代码语言:javascript
代码运行次数:0
运行
复制
// 方式1:直接构造
std::shared_ptr<类型> ptr(new 类型(构造参数));

// 方式2:使用std::make_shared (推荐)
auto ptr = std::make_shared<类型>(构造参数);

示例:

代码语言:javascript
代码运行次数:0
运行
复制
// 创建shared_ptr
auto sharedNum = std::make_shared<int>(100);

// 从unique_ptr转移所有权
std::unique_ptr<int> uniqueNum = std::make_unique<int>(50);
std::shared_ptr<int> sharedFromUnique = std::move(uniqueNum);
3. std::weak_ptr

创建格式:

代码语言:javascript
代码运行次数:0
运行
复制
// 必须从shared_ptr构造
std::weak_ptr<类型> weakPtr = sharedPtr;

示例:

代码语言:javascript
代码运行次数:0
运行
复制
auto shared = std::make_shared<std::string>("Hello");
std::weak_ptr<std::string> weak(shared);

二、核心成员函数详解

1. std::unique_ptr的核心函数

函数/操作

说明

示例

get()

返回管理的原始指针

int* raw = uptr.get();

reset()

释放当前对象,可接管新对象

uptr.reset(); 或 uptr.reset(new int(5));

release()

释放所有权,返回原始指针

int* raw = uptr.release();

operator->

访问成员

uptr->method();

operator*

解引用

int val = *uptr;

operator bool

检查是否为空

if(uptr) { ... }

swap()

交换两个unique_ptr

uptr1.swap(uptr2);

完整示例:

代码语言:javascript
代码运行次数:0
运行
复制
#include <memory>
#include <iostream>

int main() {
    // 创建unique_ptr
    std::unique_ptr<int> uptr = std::make_unique<int>(10);
    
    // 使用get获取原始指针
    std::cout << "Value: " << *uptr.get() << "\n";
    
    // 解引用操作
    *uptr = 20;
    std::cout << "Modified value: " << *uptr << "\n";
    
    // 重置指针
    uptr.reset(new int(30));
    std::cout << "After reset: " << *uptr << "\n";
    
    // 释放所有权
    int* raw = uptr.release();
    std::cout << "After release, uptr is " << (uptr ? "not null" : "null") << "\n";
    delete raw;
    
    return 0;
}
2. std::shared_ptr的核心函数

函数/操作

说明

示例

use_count()

返回引用计数

size_t count = sptr.use_count();

unique()

检查是否是唯一所有者

if(sptr.unique()) {...}

reset()

释放当前对象,减少引用计数

sptr.reset();

get()

返回管理的原始指针

auto raw = sptr.get();

operator->

访问成员

sptr->method();

operator*

解引用

auto val = *sptr;

operator bool

检查是否为空

if(sptr) {...}

swap()

交换两个shared_ptr

sptr1.swap(sptr2);

完整示例:

代码语言:javascript
代码运行次数:0
运行
复制
#include <memory>
#include <iostream>

class Resource {
public:
    Resource() { std::cout << "Resource created\n"; }
    ~Resource() { std::cout << "Resource destroyed\n"; }
    void use() { std::cout << "Resource used\n"; }
};

int main() {
    // 创建shared_ptr
    std::shared_ptr<Resource> sptr1 = std::make_shared<Resource>();
    
    // 复制shared_ptr
    {
        auto sptr2 = sptr1;
        std::cout << "Use count inside block: " << sptr1.use_count() << "\n";
        sptr2->use();
    }
    
    std::cout << "Use count outside block: " << sptr1.use_count() << "\n";
    
    // 重置shared_ptr
    sptr1.reset();
    std::cout << "After reset, use count: " << sptr1.use_count() << "\n";
    
    return 0;
}
3. std::weak_ptr的核心函数

函数/操作

说明

示例

lock()

创建共享指针来访问对象

auto sptr = wptr.lock();

expired()

检查对象是否已被释放

if(wptr.expired()) {...}

use_count()

返回关联shared_ptr的引用计数

auto count = wptr.use_count();

reset()

释放观察的对象

wptr.reset();

swap()

交换两个weak_ptr

wptr1.swap(wptr2);

完整示例:

代码语言:javascript
代码运行次数:0
运行
复制
#include <memory>
#include <iostream>

int main() {
    // 创建shared_ptr
    auto shared = std::make_shared<int>(42);
    
    // 创建weak_ptr
    std::weak_ptr<int> weak = shared;
    
    // 检查weak_ptr
    if (!weak.expired()) {
        if (auto locked = weak.lock()) {
            std::cout << "Value through weak_ptr: " << *locked << "\n";
            std::cout << "Use count: " << weak.use_count() << "\n";
        }
    }
    
    // 释放shared_ptr
    shared.reset();
    
    // 再次检查
    if (weak.expired()) {
        std::cout << "Object has been destroyed\n";
    }
    
    return 0;
}

三、智能指针的辅助函数

1. 比较操作

所有智能指针都支持比较操作:

代码语言:javascript
代码运行次数:0
运行
复制
std::shared_ptr<int> p1, p2;
if (p1 == p2) { ... }  // 比较管理的指针
if (p1 < p2) { ... }   // 基于管理指针的排序
2. std::swap

标准库提供了专门用于智能指针的swap函数:

代码语言:javascript
代码运行次数:0
运行
复制
std::swap(ptr1, ptr2);  // 等价于 ptr1.swap(ptr2)
3. std::owner_less

用于在关联容器中比较智能指针的所有权:

代码语言:javascript
代码运行次数:0
运行
复制
std::map<std::shared_ptr<int>, int, std::owner_less<std::shared_ptr<int>>> myMap;

四、自定义删除器

智能指针允许指定自定义的删除器,格式如下:

1. unique_ptr自定义删除器
代码语言:javascript
代码运行次数:0
运行
复制
// 函数指针形式
void IntDeleter(int* p) { delete p; }
std::unique_ptr<int, decltype(&IntDeleter)> uptr(new int, &IntDeleter);

// 函数对象形式
struct IntDeleter {
    void operator()(int* p) { delete p; }
};
std::unique_ptr<int, IntDeleter> uptr(new int);
2. shared_ptr自定义删除器
代码语言:javascript
代码运行次数:0
运行
复制
void FileDeleter(FILE* fp) { if(fp) fclose(fp); }
std::shared_ptr<FILE> filePtr(fopen("test.txt", "r"), FileDeleter);

五、类型转换函数

C++标准库提供了与智能指针配合使用的类型转换函数:

代码语言:javascript
代码运行次数:0
运行
复制
std::shared_ptr<Derived> derived = std::make_shared<Derived>();
std::shared_ptr<Base> base = std::static_pointer_cast<Base>(derived);
std::shared_ptr<Derived> derived2 = std::dynamic_pointer_cast<Derived>(base);
std::shared_ptr<void> voidPtr = std::reinterpret_pointer_cast<void>(derived);

六、智能指针使用的最佳实践

创建方式选择

代码语言:javascript
代码运行次数:0
运行
复制
// 推荐
auto ptr = std::make_shared<MyClass>(args);
auto uptr = std::make_unique<MyClass>(args);

// 不推荐(可能造成内存泄漏)
std::shared_ptr<MyClass> ptr(new MyClass(args));

参数传递

代码语言:javascript
代码运行次数:0
运行
复制
// 只读访问:传递const引用
void func(const std::shared_ptr<MyClass>& ptr);

// 需要取得所有权:按值传递
void func(std::unique_ptr<MyClass> ptr);

避免混用

代码语言:javascript
代码运行次数:0
运行
复制
// 危险:可能导致双重删除
MyClass* raw = new MyClass();
std::shared_ptr<MyClass> p1(raw);
std::shared_ptr<MyClass> p2(raw);  // 错误!

循环引用解决方案

代码语言:javascript
代码运行次数:0
运行
复制
class Node {
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev;  // 使用weak_ptr打破循环
};

通过掌握这些使用格式和成员函数,你可以更加安全高效地在C++程序中使用智能指针进行内存管理。记住,智能指针的目的是让资源管理更加自动化,但前提是你要正确使用它们。


小疑问:std::make_unique(10)中,如果<>中是一个自定义的类,那么()中的参数会被调用至这个类的构造函数吗?

是的,当使用 std::make_unique 创建一个自定义类的对象时,() 中的参数会被完美转发(perfect-forward)到该类的构造函数。这是 std::make_unique 的一个重要特性。

详细解释

对于 std::make_unique<ClassName>(args...)

  1. 模板参数 <ClassName>:指定要创建的对象类型
  2. 构造函数参数 (args...):会被转发给 ClassName 的构造函数
使用示例
代码语言:javascript
代码运行次数:0
运行
复制
#include <memory>
#include <iostream>

class MyClass {
public:
    int value;
    std::string name;
    
    // 构造函数1
    MyClass(int v, const std::string& n) : value(v), name(n) {
        std::cout << "Constructed MyClass(" << value << ", " << name << ")\n";
    }
    
    // 构造函数2
    explicit MyClass(int v) : value(v), name("default") {
        std::cout << "Constructed MyClass(" << value << ")\n";
    }
    
    ~MyClass() {
        std::cout << "Destroyed MyClass(" << value << ", " << name << ")\n";
    }
};

int main() {
    // 调用构造函数1
    auto obj1 = std::make_unique<MyClass>(42, "example");
    
    // 调用构造函数2
    auto obj2 = std::make_unique<MyClass>(100);
    
    // 输出对象内容
    std::cout << "obj1: " << obj1->value << ", " << obj1->name << "\n";
    std::cout << "obj2: " << obj2->value << ", " << obj2->name << "\n";
    
    return 0;
    // obj1和obj2在这里会自动销毁
}
输出结果
代码语言:javascript
代码运行次数:0
运行
复制
Constructed MyClass(42, example)
Constructed MyClass(100)
obj1: 42, example
obj2: 100, default
Destroyed MyClass(100, default)
Destroyed MyClass(42, example)
关键点
  1. 参数完美转发make_unique 使用完美转发,可以保持参数的值类别(左值/右值)
  2. 构造选择:会根据参数数量和类型自动选择最匹配的构造函数
  3. 异常安全:相比直接 newmake_unique 提供了更强的异常安全保证
  4. 效率优势make_unique 通常比直接使用 new 更高效
与直接使用 new 的对比
代码语言:javascript
代码运行次数:0
运行
复制
// 使用make_unique(推荐)
auto ptr1 = std::make_unique<MyClass>(42, "hello");

// 直接使用new(不推荐)
std::unique_ptr<MyClass> ptr2(new MyClass(42, "hello"));

make_unique 的优势:

  1. 更简洁
  2. 避免了显式的 new
  3. 更安全(防止内存泄漏)
  4. 通常更高效(可能允许编译器优化)
特殊构造函数

即使是特殊构造函数也能正常工作:

代码语言:javascript
代码运行次数:0
运行
复制
class Special {
public:
    Special(std::initializer_list<int> list) { /*...*/ }
    Special(int, double) { /*...*/ }
};

// 使用初始化列表构造函数
auto sp1 = std::make_unique<Special>({1, 2, 3});

// 使用多参数构造函数
auto sp2 = std::make_unique<Special>(10, 3.14);
总结

std::make_unique<ClassName>(args...) 中的 args... 确实会被转发到 ClassName 的构造函数:

  • 参数数量和类型决定调用哪个构造函数
  • 支持所有形式的构造函数(包括默认构造、参数化构造、移动构造等)
  • 是现代C++中创建 unique_ptr 的推荐方式
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-07-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C++智能指针使用格式与核心函数详解
    • 一、智能指针的基本使用格式
      • 1. std::unique_ptr
      • 2. std::shared_ptr
      • 3. std::weak_ptr
    • 二、核心成员函数详解
      • 1. std::unique_ptr的核心函数
      • 2. std::shared_ptr的核心函数
      • 3. std::weak_ptr的核心函数
    • 三、智能指针的辅助函数
      • 1. 比较操作
      • 2. std::swap
      • 3. std::owner_less
    • 四、自定义删除器
      • 1. unique_ptr自定义删除器
      • 2. shared_ptr自定义删除器
    • 五、类型转换函数
    • 六、智能指针使用的最佳实践
      • 详细解释
      • 使用示例
      • 输出结果
      • 关键点
      • 与直接使用 new 的对比
      • 特殊构造函数
      • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档