【C++入门到精通】智能指针 auto_ptr、unique_ptr简介及C++模拟实现 [ C++入门 ]

简介: 【C++入门到精通】智能指针 auto_ptr、unique_ptr简介及C++模拟实现 [ C++入门 ]

引言

在 C++ 中,智能指针是一种非常重要的概念,它能够帮助我们自动管理动态分配的内存,避免出现内存泄漏等问题。在上一篇文章中,我们了解了智能指针的基本概念和原理,本篇文章将继续介绍 auto_ptr 和 unique_ptr 两种智能指针的概念及其在 C++ 中的模拟实现。通过学习这些内容,您将更好地理解智能指针的不同类型和使用场景,进一步提高程序的安全性和可靠性。让我们一起探索C++智能指针的精彩世界!

一、std::auto_ptr

🔴std::auto_ptr官方文档

1. 简介

std::auto_ptr 是 C++ 标准库中提供的一种智能指针类型,用于管理动态分配的内存资源。

std::auto_ptr 的主要特点是拥有所有权语义的转移。当一个 auto_ptr 对象拥有某个内存资源时,它可以将这个所有权转移给另一个 auto_ptr 对象。这意味着当一个 auto_ptr 对象被赋值给另一个 auto_ptr 对象或者被传递给一个函数时,原来的 auto_ptr 对象将不再拥有该资源,而是转移到了新的对象上。

std::auto_ptr 类的定义在 <memory> 头文件中。使用 auto_ptr 需要包含该头文件,并使用 std 命名空间。

2. 使用示例

✅我们可以使用 auto_ptr 来管理动态分配的整型对象:

#include <iostream>
#include <memory>

int main() {
    std::auto_ptr<int> ptr(new int(42));
    
    std::cout << *ptr << std::endl;  // 输出:42
    
    std::auto_ptr<int> ptr2 = ptr;  // 所有权转移
    
    std::cout << *ptr2 << std::endl;  // 输出:42
    // std::cout << *ptr << std::endl;  // 错误!ptr 不再拥有资源

    return 0;
}

🚨🚨注意:当一个 auto_ptr 对象转移所有权后,原来的对象将变为一个空指针。这可能导致程序出现意外的行为,因此需要谨慎使用。此外,std::auto_ptr 并不支持数组类型的内存资源管理,它只适用于单个对象。如果需要管理动态分配的数组,应该使用其他智能指针类型,如 std::unique_ptr 或 std::shared_ptr。

总之,std::auto_ptr 是 C++ 标准库中提供的一种智能指针类型,具有所有权转移的特性。然而,由于其存在一些潜在的问题,已经在 C++17 中被废弃,推荐使用更先进的智能指针类型来代替。

3. C++模拟实现

template<class T>
class auto_ptr
{
public:
    // 构造函数,接受一个指向动态分配的资源的指针
    auto_ptr(T* ptr)
        : _ptr(ptr)
    {}

    // 析构函数,在对象销毁时释放资源
    ~auto_ptr()
    {
        if (_ptr)
        {
            cout << "delete:" << _ptr << endl;
            delete _ptr;
        }
    }

    // 拷贝构造函数,用于管理权转移
    auto_ptr(auto_ptr<T>& ap)
        : _ptr(ap._ptr)
    {
        ap._ptr = nullptr;
    }

    // 解引用操作符,返回所管理资源的引用
    T& operator*()
    {
        return *_ptr;
    }

    // 成员访问操作符,返回所管理资源的指针
    T* operator->()
    {
        return _ptr;
    }
private:
    T* _ptr;  // 指向动态分配的资源的指针
};

这段代码是一个简化版的 auto_ptr 类的实现,用于演示 auto_ptr 的基本工作原理。它是一个模板类,可以管理任意类型的动态分配的内存资源。

⭕该类包含了以下成员函数和成员变量:

  • 构造函数:接受一个指向动态分配的资源的指针,并将其存储在私有成员变量 _ptr 中。
  • 析构函数:在对象销毁时释放资源,即调用 delete 操作符删除 _ptr 指向的内存。
  • 拷贝构造函数:用于管理权转移,将另一个 auto_ptr 对象的资源转移到当前对象中,并将原对象的指针置为空。
  • operator*:用于解引用 auto_ptr 对象,返回所管理资源的引用。
  • operator->:用于访问 auto_ptr 所管理资源的成员。

🚨🚨再次提醒当一个 auto_ptr 对象转移所有权后,原来的对象将变为一个空指针。这可能导致程序出现意外的行为,因此需要谨慎使用

二、std::unique_ptr

🔴std::unique_ptr官方文档

1. 简介

std::unique_ptr 是 C++11 中引入的一种智能指针,它是一个轻量级的、不可拷贝的指针类型。与传统的裸指针不同,std::unique_ptr 通过 RAII 的方式来管理动态分配的内存资源,从而实现自动资源释放和防止内存泄漏

使用 std::unique_ptr 可以避免手动管理动态分配的内存资源,因为 std::unique_ptr 自身就拥有资源的所有权,当 std::unique_ptr 被销毁时,它所管理的资源也会被自动释放。这使得代码更加简洁、安全和易于维护。


std::unique_ptr 有以下几个主要特点:

  1. 不支持拷贝和赋值操作,即不能直接复制或赋值一个 std::unique_ptr 对象,只能通过移动语义或者 std::move 函数来转移资源的所有权;
  2. 在默认情况下,std::unique_ptr 使用 delete 操作符来释放所管理的资源,但也可以通过自定义删除器来实现对资源的自定义释放操作;
  3. 支持使用 lambda 表达式来实现自定义删除器,从而更加灵活地管理资源。

2. 使用示例

下面是一个简单的示例,演示了 std::unique_ptr 的基本使用方法:

#include <iostream>
#include <memory>

int main()
{
    // 使用 std::unique_ptr 来管理动态分配的 int 类型对象
    std::unique_ptr<int> uptr(new int(42));

    // 解引用操作符,返回所管理资源的引用
    std::cout << *uptr << std::endl;

    // 成员访问操作符,返回所管理资源的指针
    int* p = uptr.get();
    std::cout << *p << std::endl;

    // 试图复制或赋值 unique_ptr 对象会编译错误
    // std::unique_ptr<int> uptr2 = uptr; // Error

    // 转移资源所有权
    std::unique_ptr<int> uptr2 = std::move(uptr);
    std::cout << *uptr2 << std::endl;

    return 0;
}

输出结果为

42
42
42

🚨🚨注意:由于 std::unique_ptr 是一个模板类,可以管理任意类型的动态分配的内存资源,因此使用时需要显式指定模板参数。另外,对于数组的动态分配内存资源的管理,建议使用 std::unique_ptr 的数组版本 std::unique_ptr<T[]>。

3. C++模拟实现

template<class T>
class unique_ptr
{
public:
    // 构造函数,接受一个裸指针作为参数
    unique_ptr(T* ptr)
        :_ptr(ptr)
    {}

    // 析构函数,释放资源
    ~unique_ptr()
    {
        if (_ptr)
        {
            cout << "delete:" << _ptr << endl;
            delete _ptr;
        }
    }

    // 重载解引用操作符,返回资源的引用
    T& operator*()
    {
        return *_ptr;
    }

    // 重载成员访问操作符,返回资源的指针
    T* operator->()
    {
        return _ptr;
    }

    // 防止拷贝和赋值
    // C++11思路:直接将拷贝构造函数和赋值运算符声明为删除函数
    unique_ptr(const unique_ptr<T>& up) = delete;
    unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;

    // 防止拷贝和赋值
    // C++98思路:只声明不实现,但是用的人可能会在外部强行定义,所以再加一条,声明为私有
    // private:
    // unique_ptr(const unique_ptr<T>& up);
    // unique_ptr<T>& operator=(const unique_ptr<T>& up);

private:
    T* _ptr;
};

🚩这段代码实现了一个简化版的 unique_ptr 类,具有管理动态资源的能力,并防止了拷贝和赋值操作。注释中详细解释了每个函数的作用和实现原理,以及两种防止拷贝和赋值的方式(C++11 和 C++98 思路)

温馨提示

感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!

目录
相关文章
|
10月前
|
缓存 安全 编译器
C++面试周刊(3):面试不慌,这样回答指针与引用,青铜秒变王者
《C++面试冲刺周刊》第三期聚焦指针与引用的区别,从青铜到王者级别面试回答解析,助你21天系统备战,直击高频考点,提升实战能力,轻松应对大厂C++面试。
913 132
C++面试周刊(3):面试不慌,这样回答指针与引用,青铜秒变王者
|
8月前
|
存储 算法
算法入门:专题一:双指针(有效三角形的个数)
给定一个数组,找出能组成三角形的三元组个数。利用“两边之和大于第三边”的性质,先排序,再用双指针优化。固定最大边,左右指针从区间两端向内移动,若两短边之和大于最长边,则中间所有组合均有效,时间复杂度由暴力的O(n³)降至O(n²)。
|
10月前
|
存储 C++
C++语言中指针变量int和取值操作ptr详细说明。
总结起来,在 C++ 中正确理解和运用 int 类型地址及其相关取值、设定等操纵至关重要且基础性强:定义 int 类型 pointer 需加星号;初始化 pointer 需配合 & 取址;读写 pointer 执向之处需配合 * 解引用操纵进行。
763 12
|
存储 安全 编译器
c++入门
c++作为面向对象的语言与c的简单区别:c语言作为面向过程的语言还是跟c++有很大的区别的,比如说一个简单的五子棋的实现对于c语言面向过程的设计思路是首先分析解决这个问题的步骤:(1)开始游戏(2)黑子先走(3)绘制画面(4)判断输赢(5)轮到白子(6)绘制画面(7)判断输赢(8)返回步骤(2) (9)输出最后结果。但对于c++就不一样了,在下五子棋的例子中,用面向对象的方法来解决的话,首先将整个五子棋游戏分为三个对象:(1)黑白双方,这两方的行为是一样的。(2)棋盘系统,负责绘制画面。
182 0
|
存储 缓存 C++
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
|
存储 分布式计算 编译器
C++入门基础2
本内容主要讲解C++中的引用、inline函数和nullptr。引用是变量的别名,与原变量共享内存,定义时需初始化且不可更改指向对象,适用于传参和返回值以提高效率;const引用可增强代码灵活性。Inline函数通过展开提高效率,但是否展开由编译器决定,不建议分离声明与定义。Nullptr用于指针赋空,取代C语言中的NULL。最后鼓励持续学习,精进技能,提升竞争力。
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
553 12
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
288 0