博业建站网,如何查看网站建设时间,淘宝网站的建设目标是,修改WordPress登录入口智能指针
定义
为什么需要智能指针
在C中#xff0c;动态分配内存是一项常见的任务#xff0c;但手动管理分配和释放内存可能会导致很多问题#xff0c;如内存泄漏、悬垂指针以及多次释放同一块内存等。为了避免这些问题#xff0c;引入了智能指针的概念#xff0c;它们…智能指针
定义
为什么需要智能指针
在C中动态分配内存是一项常见的任务但手动管理分配和释放内存可能会导致很多问题如内存泄漏、悬垂指针以及多次释放同一块内存等。为了避免这些问题引入了智能指针的概念它们提供了自动化的内存管理。
以下是智能指针可以解决的一些问题 智能指针的使用 及 原理
智能指针的原理
即 RAII ↓
RAII
RAIIResource Acquisition Is Initialization是一种编程技术和设计原则它通过将资源的获取与对象的初始化绑定在一起来管理资源。在使用 RAII 时资源的获取和释放操作被封装在对象的构造函数和析构函数中利用了对象的生命周期管理资源的自动分配和释放。
该技术的 基本思想
对象的构造函数负责获取资源如内存、文件句柄、数据库连接等析构函数负责释放这些资源。通过使用 RAII可以确保在对象离开作用域时无论是正常退出还是异常退出资源都会被正确释放从而避免了资源泄漏的问题。
下面利用 RAII 思想实现 一个SmartPtr 的代码
// 利用RAII思想实现的SmartPtr类
templateclass T
class SmartPtr
{
public:// 构造函数 - 获取资源SmartPtr(T* ptr nullptr):_ptr(ptr){}// 析构函数 - 释放资源~SmartPtr(){if (_ptr){cout Delete: _ptr endl;}}// 实现自定义指针类需要的函数// 重载 operator* 函数用于实现指针解引用操作允许通过对象的指针访问该指针所指向的对象。返回类型为 T表示对指向 T 类型对象的引用。// 重载 operator- 函数用于实现指针的箭头操作允许通过对象的指针直接调用该指针所指向对象的成员函数或成员变量。返回类型为 T* 表示指向 T 类型对象的指针。T operator*(){return *_ptr;}T* operator-(){return _ptr;}
private:T* _ptr;
};
上述代码实现了简要的智能指针的功能智能指针的原理即 RAII特性 重载operator*和opertaor- 具有和指针一样的行为 智能指针的种类 auto_ptr
auto_ptr是C98标准中提供的智能指针它具有独占性质意味着同一时间只能有一个auto_ptr拥有对特定对象的所有权。
当一个auto_ptr被赋值给另一个auto_ptr时所有权会被转移原来的auto_ptr将不再拥有该对象的所有权 (所有权转移) 。这种特性可以用于简单的资源管理但也容易导致潜在的问题。
由于 auto_ptr 的所有权转移特性在某些情况下可能会导致意外的行为。 例如:
如果将auto_ptr存储在标准容器中容器的复制或赋值操作会导致对象所有权的转移从而使得容器内的指针失效。
此外
auto_ptr在异常处理机制方面也存在问题如果在析构过程中抛出异常可能会导致资源泄漏。
代码分析
下面对 auto_ptr 的模拟实现展示了 auto_ptr 的性质
namespace aiyimu
{// C98: auto_ptr 有一定缺陷templateclass Tclass auto_ptr{public:auto_ptr(T* ptrnullptr) // 构造:_ptr(ptr){}~auto_ptr() // 析构{if (_ptr){cout Delete: _ptr endl;delete _ptr;}}// 指针特性T operator*(){return *_ptr;}T* operator-(){return _ptr;}// 拷贝构造auto_ptr(auto_ptrT ap):_ptr(ap._ptr){// 所有权 / 管理权 转移ap._ptr nullptr;}// 赋值重载auto_ptr operator(auto_ptrT ap){// 给自己赋值不执行操作if (this ! ap){if (_ptr) //如果_ptr指向了对象则删除其指向{cout Delete: _ptr endl;delete _ptr;}_ptr ap._ptr;ap._ptr nullptr;}return *this;}private:T* _ptr;};
}通过下面对 std::auto_ptr 的调用可以看出其缺点
void Test_auto_ptr()
{std::auto_ptrint sp1(new int);std::auto_ptrint sp2(sp1); //此时所有权转移// 此时的 sp1 悬空*sp2 20;cout *sp2 endl;cout *sp1 endl;
}该函数在调用执行后会报错原因如下
sp2通过拷贝构造函数从sp1获取了所有权。这导致原来的sp1变为悬空指针指向的内存区域不再有效。在对悬空指针sp1进行解引用操作时会导致未定义行为。因此打印*sp1的语句会产生不可预测的结果。由于std::auto_ptr的问题该代码没有正确处理资源所有权的转移和管理。
综上所述建议使用C11标准中提供的智能指针类型如std::unique_ptr、std::shared_ptr或std::weak_ptr以避免这些问题并更好地管理资源所有权和避免悬空指针的情况。 unique_ptr
unique_ptr 的性质
拥有性unique_ptr 是一个独占所有权的智能指针它禁止两个 unique_ptr 对象指向同一个对象。所有权转移与 auto_ptr 一样unique_ptr 支持所有权的转移。通过移动语义可以将一个 unique_ptr 的所有权从一个对象转移到另一个对象从而避免了资源的复制和多次删除。自动释放当 unique_ptr 被销毁或者重新赋值时它会自动删除所拥有的资源避免了内存泄漏。零开销unique_ptr 本身非常轻量不引入额外的开销且常被优化为和裸指针一样的大小和性能。
代码分析原理
通过下面的模拟实现 理解其 原理
namespace
{// unique_ptr 是一个独占所有权的智能指针它禁止两个 unique_ptr 对象指向同一个对象。// 当尝试使用拷贝构造函数或赋值重载运算符来创建或赋值 unique_ptr 对象时编译器会报错。templateclass Tclass unique_ptr{public:// 使用 delete 关键字 禁用其拷贝和赋值unique_ptr(unique_ptrT ap) delete;unique_ptrT operator(unique_ptrT ap) delete;// 构造unique_ptr(T* ptr nullptr): _ptr(ptr){}// 析构~unique_ptr(){if (_ptr){cout Delete _ptr endl;delete _ptr;_ptr nullptr;}}T operator*(){return *_ptr;}T* operator-(){return _ptr;}private: T* _ptr;};
};下面是一段 使用代码
class A
{
public:~A(){cout ~A() endl;}//private:int _a1 0;int _a2 0;
};void test_unique_ptr()
{aiyimu::unique_ptrA up1(new A);// 这样的拷贝行为违反了 unique_ptr 的意图unique_ptr 应该是独占资源的智能指针// aiyimu::unique_ptrA up2(up1);up1-_a1;up1-_a2;cout up1-_a1: up1-_a1 endl;cout up1-_a2: up1-_a2 endl;// 输出结果: 1 1
}总结unique_ptr 是一种独占所有权的智能指针不允许直接进行拷贝行为。如果需要共享资源可以使用 shared_ptr 来实现。 shared_ptr
性质
共享性shared_ptr 可以与其他 shared_ptr 共享所管理的资源。通过内部的引用计数机制shared_ptr在其内部给每个资源都维护了着一份计数用来记录该份资源被几个对象共享。拷贝语义shared_ptr 支持拷贝构造函数和拷贝赋值运算符。当一个 shared_ptr 被拷贝给另一个 shared_ptr 时它们将共享同一个资源引用计数会增加。自动释放当最后一个引用计数为零时即没有任何 shared_ptr 实例指向某个资源时shared_ptr 自动释放资源。这可以避免了资源泄漏。循环引用处理shared_ptr 使用弱引用计数weak_ptr来解决循环引用问题。循环引用是指两个或多个对象相互持有对方的 shared_ptr 实例导致引用计数无法归零。通过将其中一个 shared_ptr 转换为 weak_ptr可以打破循环引用使资源正确释放。定制删除器与 unique_ptr 类似shared_ptr 也 支持定制的删除器 以实现对不同类型资源的特殊释放操作。
其中的重点在于引用计数
对象被销毁时(析构函数调用)资源不再使用对象的引用计数减一。如果引用计数是0就说明自己是最后一个使用该资源的对象必须释放该资源如果引用计数不是0就说明仍有其他对象在使用该份资源不能释放该资源否则其他对象就成野指针了
代码分析
namespace aiyimu
{// shared_ptr 是 C 中一种共享所有权的智能指针。// 与 unique_ptr 只能由一个对象拥有所有权不同shared_ptr 允许多个 shared_ptr 对象同时管理同一个对象templateclass Tclass shared_ptr{public:// 构造shared_ptr(T* ptr nullptr):_ptr(ptr),_pCount(new int(1)){}// 析构~shared_ptr(){Release();}// 拷贝构造shared_ptr(const shared_ptrT sp):_ptr(sp._ptr),_pCount(sp._pCount){(*_pCount); // 计数}// 赋值重载shared_ptrT operator(const shared_ptrT sp){// 自己赋值自己不做操作if (_ptr sp._ptr){return *this;}Release();// 共享新资源计数_ptr sp._ptr;_pCount sp._pCount;(*_pCount);//返回*thisreturn *this;}T operator*(){assert(_ptr ! nullptr); // 断言指针非空return *_ptr;}T* operator-(){assert(_ptr ! nullptr); // 断言指针非空return _ptr;}// 返回计数个数int use_count(){return *_pCount;}// 获取指针T* get() const{return _ptr;}// Release 函数用于释放资源并销毁 shared_ptr 对象void Release() {if (--(*_pCount) 0) {cout Delete: _ptr endl;delete _ptr;delete _pCount;}}private:T* _ptr;int* _pCount; // 引用计数};
};下面的代码将展示 shared_ptr 的使用 和 性质验证
void test_shared_ptr1()
{aiyimu::shared_ptrA sp1(new A);aiyimu::shared_ptrA sp2(sp1);aiyimu::shared_ptrA sp3(sp1);sp1-_a1;sp1-_a2;cout sp2-_a1 : _a2 - sp2-_a1 : sp2-_a2 endl; // 1 1// shared_ptr 使sp1 sp2 共享一块内存即两者的_a1_a2的值是同步的sp2-_a1;sp2-_a2;cout sp1-_a1 : _a2 - sp1-_a1 : sp1-_a2 endl; // 2 2
}下面将介绍 与 shared_ptr 配合使用的 weak_ptr weak_ptr
weak_ptr 是 C 标准库C11 及以后版本中与 shared_ptr 配合使用的智能指针类 用于解决 shared_ptr 的循环引用问题 。
特性
弱引用weak_ptr 是一种弱引用它可以观测但不拥有一个由 shared_ptr 管理的对象。通过 shared_ptr 创建 weak_ptr可以同时存在多个 weak_ptr 实例观测同一个资源。不会增加引用计数weak_ptr 不会增加所管理资源的引用计数。即使存在 weak_ptr 对象观测某个资源资源的引用计数也不会增加因此不会影响资源的生命周期。检查资源是否有效可以使用 expired() 函数检查 weak_ptr 所观测的资源是否还存在。如果资源已经被释放即引用计数为零expired() 返回 true否则返回 false。获取共享指针可以使用 lock() 函数将 weak_ptr 转换为 shared_ptr得到与之关联的共享指针。如果资源仍然存在则返回一个有效的 shared_ptr如果资源已被释放则返回一个空的 shared_ptr。
代码分析
下面是一个简化版本的 weak_ptr 的模拟实现
// weak_ptr 是 C 中一种弱引用智能指针用于解决 shared_ptr 的循环引用问题。templateclass Tclass weak_ptr{public:weak_ptr():_ptr(nullptr){}// 该构造函数接受一个shared_ptrT类型的参数sp将其内部指针通过get()函数获取后赋值给_ptr。// 可以创建一个weak_ptr对象来观测所传入的shared_ptr所管理的资源。weak_ptr(const shared_ptrT sp):_ptr(sp.get()){}// 该构造函数接受一个weak_ptrT类型的参数wp将其内部指针直接赋值给_ptr。// 可以创建一个新的weak_ptr对象其观测的资源与原wp对象相同。weak_ptr(const weak_ptrT wp):_ptr(wp._ptr){}T operator*(){return *_ptr;}T* operator-(){return _ptr;}// 赋值weak_ptrT operator(const shared_ptrT sp){_ptr sp.get();return *this;}private:T* _ptr;};