c++智能指针

一、 概念

C++堆内存对象在new之后使用,如果忘记delete则会产生内存泄漏的问题。诸如Java、C#等语言直接提供垃圾回收机制来处理不使用的对象,因此在C++98中引入了智能指针的概念,并在C++11中趋于完善。使用智能指针可以让堆内存对象不调用delete就能被销毁。智能指针的原理是通过一个栈内存的智能指针对象,控制被管理的堆内存对象生命周期,当栈内存的智能指针对象销毁时,在析构函数中释放被管理管理的堆内存对象。这样程序员就不需要手动调用delete释放堆内存对象了。
C++中有四种只能指针:

  • auto_ptr 自动指针(C++98),已废弃。
  • unique_ptr 唯一指针(C++11)
  • shared_ptr 共享指针(C++11)
  • weak_ptr 虚指针(C++11)v

智能指针的使用需要引入头文件 #include <memory>

二、auto_ptr

#include <iostream>

#include <memory> // 头文件

using namespace std;

/**

* @brief The Source class

* 被智能指针管理的堆内存资源对象类

*/

class Source

{

private:

string name;

public:

Source(string name):name(name)

{

cout << name << "构造函数" << endl;

}

~Source()

{

cout << name << "析构函数" << endl;

}

void show()

{

cout << name << "成员函数" << endl;

}

};

int main()

{

Source* src = new Source("A");

// 创建一个智能指针对象ap1,管理对象A

auto_ptr<Source> ap1(src);

// 上面的操作可以一步完成

auto_ptr<Source> ap2(new Source("B"));

// 被管理的对象可以通过getter拿出来

ap2.get()->show(); // B

src = ap2.get();

src->show(); // B

// delete src; 乱码:对象内存不能释放两次

// 解除ap1对A的管理,发返回值为对象A

src = ap1.release();

src->show(); // A

delete src; // 因为A回到手动模式,所以可以delete销毁

// 解除ap2对B的管理,同时销毁B

ap2.reset();

auto_ptr<Source> ap3(new Source("C"));

// 使用新的资源对象D替代C,C被销毁

ap3.reset(new Source("D"));

cout << "主函数结束" << endl;

return 0;

}
auto_ptr真正容易误用的地方是其不常用的复制语义(拷贝构造函数和赋值运算符),与浅拷贝的区别是,auto_ptr的复制语义不会让多个auto_ptr共享一个资源,而是会出现控制权的转移。

#include <iostream>

#include <memory> // 头文件

using namespace std;

/**

* @brief The Source class

* 被智能指针管理的堆内存资源对象类

*/

class Source

{

private:

string name;

public:

Source(string name):name(name)

{

cout << name << "构造函数" << endl;

}

~Source()

{

cout << name << "析构函数" << endl;

}

void show()

{

cout << name << "成员函数" << endl;

}

};

int main()

{

auto_ptr<Source> ap1(new Source("A"));

cout << ap1.get() << endl; // 0xfa1060

auto_ptr<Source> ap2(ap1); // 拷贝构造函数

cout << ap1.get() << " " << ap2.get() << endl; // 0 0xfa1060

auto_ptr<Source> ap3;

ap3 = ap2; // 赋值运算符

cout << ap1.get() << " " << ap2.get()

<< " " << ap3.get() << endl; // 0 0 0xfa1060

cout << "主函数结束" << endl;

return 0;

}

三、unique_ptr

作为对auto_ptr的改进,unique_ptr对其持有的堆内存对象具有唯一控制权,即不可以转移到其他智能指针管理。


上面的代码中可以看到,所有复制语义都被屏蔽了。

unique_ptr可以增加move函数达到之前控制权转移的效果。

#include <iostream>

#include <memory> // 头文件

using namespace std;

/**

* @brief The Source class

* 被智能指针管理的堆内存资源对象类

*/

class Source

{

private:

string name;

public:

Source(string name):name(name)

{

cout << name << "构造函数" << endl;

}

~Source()

{

cout << name << "析构函数" << endl;

}

void show()

{

cout << name << "成员函数" << endl;

}

};

int main()

{

unique_ptr<Source> up1(new Source("A"));

cout << up1.get() << endl; // 0xfa1060

unique_ptr<Source> up2(move(up1)); // 拷贝构造函数

cout << up1.get() << " " << up2.get() << endl; // 0 0xfa1060

unique_ptr<Source> up3;

up3 = move(up2); // 赋值运算符

cout << up1.get() << " " << up2.get()

<< " " << up3.get() << endl; // 0 0 0xfa1060

// 如果两个unique_ptr对象交换资源,可以使用swap函数

unique_ptr<Source> up4(new Source("B"));

unique_ptr<Source> up5(new Source("C"));

up4.swap(up5);

up4.get()->show();

up5.get()->show();

cout << "主函数结束" << endl;

return 0;

}

四、shared_ptr

shared_ptr可以把持有的资源在多个智能指针之间共享。

shared_ptr除了可以使用构造函数创建外,还可以使用make_shared函数创建。

#include <iostream>

#include <memory> // 头文件

using namespace std;

/**

* @brief The Source class

* 被智能指针管理的堆内存资源对象类

*/

class Source

{

private:

string name;

public:

Source(string name):name(name)

{

cout << name << "构造函数" << endl;

}

~Source()

{

cout << name << "析构函数" << endl;

}

void show()

{

cout << name << "成员函数" << endl;

}

};

int main()

{

shared_ptr<Source> sp1(new Source("A"));

shared_ptr<Source> sp2 = make_shared<Source>("B");

cout << "主函数结束" << endl;

return 0;

}
make_shared函数创建相比于普通的构造函数:

  • 性能更好
  • 更加安全
  • 可能内存延迟释放

更推荐使用make_shared函数创建shared_ptr对象。

每个shared_ptr内部都多一个隐藏的成员变量,这个变量通常被称为"引用计数",每多一个shared_ptr持有资源就计数+1,每少一个shared_ptr持有资源就计数-1,当计数从1变为0时,释放资源。

持有相同资源的shared_ptr对象,引用计数一定相同

#include <iostream>

#include <memory> // 头文件

using namespace std;

/**

* @brief The Source class

* 被智能指针管理的堆内存资源对象类

*/

class Source

{

private:

string name;

public:

Source(string name):name(name)

{

cout << name << "构造函数" << endl;

}

~Source()

{

cout << name << "析构函数" << endl;

}

void show()

{

cout << name << "成员函数" << endl;

}

};

int main()

{

shared_ptr<Source> sp1 = make_shared<Source>("A");

cout << sp1.use_count() << endl; // 1

shared_ptr<Source> sp2(sp1); // 拷贝构造函数

cout << sp1.use_count() << " " << sp2.use_count() << endl; // 2 2

sp2.reset(); // 解除sp2对A的管理

cout << sp1.use_count() << " " << sp2.use_count() << endl; // 1 0

shared_ptr<Source> sp3;

sp3 = sp1; // 赋值运算符

cout << sp1.use_count() << " " << sp3.use_count() << endl; // 2 2

shared_ptr<Source> sp4 = sp3; // 拷贝构造函数

cout << sp1.use_count() << endl; // 3

// shared_ptr<Source> sp5(sp4.get()); 错误

sp4.get()->show();

{ // 局部代码块

shared_ptr<Source> sp6;

sp6 = sp1;

cout << sp1.use_count() << endl; // 4

}

cout << sp1.use_count() << endl; // 3

cout << "主函数结束" << endl;

return 0;

}

五、weak_ptr

weak_ptr本身无法独立工作,但是可以配合shared_ptr进行工作,weak_ptr对资源对象的管理是一种"弱引用",因为不会影响引用计数。

#include <iostream>

#include <memory> // 头文件

using namespace std;

/**

* @brief The Source class

* 被智能指针管理的堆内存资源对象类

*/

class Source

{

private:

string name;

public:

Source(string name):name(name)

{

cout << name << "构造函数" << endl;

}

~Source()

{

cout << name << "析构函数" << endl;

}

void show()

{

cout << name << "成员函数" << endl;

}

};

int main()

{

shared_ptr<Source> sp1 = make_shared<Source>("A");

weak_ptr<Source> wp1 = sp1;

cout << sp1.use_count() << endl; // 1

cout << wp1.use_count() << endl; // 1

// wp1.get(); 无法获取资源

sp1.reset(); // 计数1→0,对象A销毁

cout << wp1.use_count() << endl; // 0

cout << "主函数结束" << endl;

return 0;

}
可以使用expired函数检测虚指针对资源持有的有效性,即资源对象是否已销毁。如果资源对象没有被销毁,可以使用lock函数生成一个shared_ptr对象共享资源。

#include <iostream>

#include <memory> // 头文件

using namespace std;

/**

* @brief The Source class

* 被智能指针管理的堆内存资源对象类

*/

class Source

{

private:

string name;

public:

Source(string name):name(name)

{

cout << name << "构造函数" << endl;

}

~Source()

{

cout << name << "析构函数" << endl;

}

void show()

{

cout << name << "成员函数" << endl;

}

};

int main()

{

shared_ptr<Source> sp1 = make_shared<Source>("A");

weak_ptr<Source> wp1 = sp1;

// sp1.reset();

shared_ptr<Source> sp2;

// lock能用的前提是资源对象还在

if(wp1.expired())

{

cout << "资源已失效" << endl;

}else

{

sp2 = wp1.lock(); // 返回一个shared_ptr<Source>,但是是临时的,需要保存下来

cout << sp1.use_count() << " " <<

sp2.use_count() <<" " << wp1.use_count() << endl; // 2 2 2

}

cout << "主函数结束" << endl;

return 0;

}

相关推荐
Code哈哈笑2 分钟前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
程序猿进阶6 分钟前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
qq_433618448 分钟前
shell 编程(二)
开发语言·bash·shell
闻缺陷则喜何志丹11 分钟前
【C++动态规划 图论】3243. 新增道路查询后的最短距离 I|1567
c++·算法·动态规划·力扣·图论·最短路·路径
charlie11451419122 分钟前
C++ STL CookBook
开发语言·c++·stl·c++20
袁袁袁袁满22 分钟前
100天精通Python(爬虫篇)——第113天:‌爬虫基础模块之urllib详细教程大全
开发语言·爬虫·python·网络爬虫·爬虫实战·urllib·urllib模块教程
ELI_He99929 分钟前
PHP中替换某个包或某个类
开发语言·php
小林熬夜学编程33 分钟前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
m0_7482361136 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
倔强的石头10644 分钟前
【C++指南】类和对象(九):内部类
开发语言·c++