c++系列 —— 智能指针auto_ptr和unique_ptr

往期地址:

本期主题:
智能指针



1.智能指针概述

1.解决了什么问题,智能在哪里

智能指针解决的问题:

解决了内存泄漏的问题,对于申请在堆上的内存(c语言是malloc,c++是new),程序员不需要主动释放,智能指针自动释放

2.对智能指针的原理猜想

  1. 我们都知道栈上的内存能够自动释放,所以猜测智能指针的底层应该是类似于栈的方式
  2. 栈上的变量,创建时自动调用构造函数,退出时自动调用析构函数

按照这样的方式我们来写一份测试代码:

#include <iostream>
using namespace std;
//构造函数申请内存,析构函数释放
class smart_pointer
{
public:
    smart_pointer()
    {
        cout << "smart_pointer()" << endl;
        p = NULL;
    }

    smart_pointer(int size)
    {
        cout << "smart_pointer(int size)" << endl;
        p = new int[size];
    }

    ~smart_pointer()
    {
        cout << "~smart_pointer()" << endl;

        if (NULL != p)
            delete[] p;
    }

    void test_func()
    {
        if (p != NULL) {
            p[0] = 1;
            cout << "p[0] val is " << p[0] << endl;
        }
    }

private:
    int *p;

};

int main(void)
{
    //这里选择栈上的局部变量,这样就能够自动调用构造和析构函数
    smart_pointer sp_instance(3);
    sp_instance.test_func();

    return 0;
}

运行结果:
在这里插入图片描述

2.智能指针使用

详细内容可以参考cppreference的动态内存管理章节中,cpprefernce动态内存管理

2.1 auto_ptr

auto_ptr定义:

template< class T > class auto_ptr;

使用示例:

#include <iostream>
#include <memory>
class people
{
public:
    people() {
        cout << "people()" << endl;
    }

    people(string name) {
        this->name = name;
        cout << "people(string name)" << endl;
    }

    ~people() {
        cout << "~people()" << endl;
    }

    void print_name(void)
    {
        cout << "name: " << this->name << endl;
    }
private:
    string name;

};

int main(void)
{
    //p1是people对象的智能指针
    auto_ptr<people> p1(new people("jason"));
    p1->print_name();
}

运行结果:
//这里需要使用 --std=c++98的原因是 autoptr在C++17中移除了,所以需要告诉编译器用c++98的方式去编译,不然会编译报错
在这里插入图片描述

2.2 unique_ptr

unique_ptr的定义如下:

template<
    class T,
    class Deleter = std::default_delete<T>
> class unique_ptr;

1. 构造函数使用

unique_ptr有多种构造函数,详细的描述参考cpprefernce,这里我们讲几种:

struct Foo {
	Foo() {cout << "Foo default ctor\n";}
	~Foo() {cout << "~Foo dtor\n";}
};

//删除器
struct D {
	D() {cout << "D default ctor\n";}
	D(const D&) {cout << "D copy ctor\n";}
	void operator()(Foo* p) const {
		cout << "D is deleting Foo\n";
		delete p;
	};
};

int main(void)
{
	cout << "Example constructor(1)...\n";
	unique_ptr<Foo> up1;
	unique_ptr<Foo> up1b(nullptr);
	
	cout << "Example constructor(2)...\n";
	//添加代码括号,改变up2变量的生命周期,退出括号时就释放up2
	{
		unique_ptr<Foo> up2(new Foo);
	}

	cout << "Example constructor(3)...\n";
	D d;
	{
		//d是类D的对象,up3的构造函数以d作为了参数,所以会调用D的复制构造函数
		unique_ptr<Foo, D> up3(new Foo, d);
	}
	{
		//传递的参数已经是引用了,不需要像上面一样再调用复制构造
		unique_ptr<Foo, D&> up4(new Foo, d);
	}
	
	//3个数组,构造调用3次,析构也会调用3次
	cout << "Example array constructor...\n";
	{
		unique_ptr<Foo[]> up(new Foo[3]);
	}
	return 0;
}

测试结果:

$ ./a.out 
Example constructor(1)...
Example constructor(2)...
Foo default ctor
~Foo dtor
Example constructor(3)...
D default ctor
Foo default ctor
D copy ctor
D is deleting Foo
~Foo dtor
Foo default ctor
D is deleting Foo
~Foo dtor
Example array constructor...
Foo default ctor
Foo default ctor
Foo default ctor
~Foo dtor
~Foo dtor
~Foo dtor

2. 管理器 release/reset/swap

这几个管理去都比较简单,直接看代码即可:

struct Foo {
	int val;

	Foo() {cout << "Foo default ctor\n";}
	Foo(int _val):val(_val) {cout << "Foo int val\n";}
	~Foo() {cout << "~Foo dtor\n";}
};

//删除器
struct D {
	D() {cout << "D default ctor\n";}
	D(const D&) {cout << "D copy ctor\n";}
	void operator()(Foo* p) const {
		cout << "D is deleting Foo\n";
		delete p;
	};
};

int main(void)
{
	//1.release 释放被管理对象
	cout << "Example 1, release....\n";
	//创建被管理对象的指针
	unique_ptr<Foo> up(new Foo());
	//使用release,释放被管理对象的所有权,给另外一个指针(另外一个指针并非智能指针)
	Foo *fp = up.release();
	
	assert (up.get() == nullptr);
	cout << "Foo is no longer owned by unique_ptr...\n";
	delete fp;
	
	//2.reset 替换被管理对象
	//与release的区别在于,这里会先析构掉旧的智能指针,然后创建新的
	cout << "Example 2, reset....\n";
	unique_ptr<Foo> up2(new Foo());
	
	cout << "new Foo, start reset--------\n";
	up2.reset(new Foo());
	cout << "new Foo, reset done--------\n";
	
	//3.swap,用来交换 *this和另一unique_ptr对象other所管理的内容
	//void swap(unique_ptr& other) noexcept;
	cout << "Example 3, swap....\n";
	unique_ptr<Foo> up3a(new Foo(1));
	unique_ptr<Foo> up3b(new Foo(2));
	
	up3a.swap(up3b);
	
	cout << "up3a->val:" << up3a->val << endl;
	cout << "up3b->val:" << up3b->val << endl;
	
	return 0;
}

测试结果:

Example 1, release....
Foo default ctor
Foo is no longer owned by unique_ptr...
~Foo dtor
Example 2, reset....
Foo default ctor
new Foo, start reset--------
Foo default ctor
~Foo dtor
new Foo, reset done--------
Example 3, swap....
Foo int val
Foo int val
up3a->val:2
up3b->val:1
~Foo dtor
~Foo dtor
~Foo dtor

3. 观察器 get/get_deleter/operator bool

直接看代码:

struct Foo {
	int val;

	Foo() {cout << "Foo default ctor\n";}
	Foo(int _val):val(_val) {cout << "Foo int val\n";}
	~Foo() {cout << "~Foo dtor\n";}
};

//删除器
struct D {
	void test_func(void) {
		cout << "test_func" << endl;
	};
	
	D() {cout << "D default ctor\n";}
	D(const D&) {cout << "D copy ctor\n";}
	void operator()(Foo* p) const {
		cout << "D is deleting Foo\n";
		delete p;
	};
};
int main(void)
{

	unique_ptr<string> up1(new string("hello"));
	
	string *p1 = up1.get();
	cout << *p1 << endl;

	cout << "Example 2, get_deleter ....\n";
	//2.get_deleter返回被管理对象的删除器
	unique_ptr<Foo, D> up2(new Foo, D());
	D& d = up2.get_deleter();
	d.test_func();
	
	//3.operator bool,就是将指针对象进行运算符重载,重载成一个bool量
	cout << "Example 3, operator bool ....\n";
	if ((up1) && (up2))
	{
		cout << true << endl;
	}
	
	return 0;
}

测试结果:

$ ./a.out
Example 1, get....
hello
Example 2, get_deleter ....
D default ctor
Foo default ctor
D copy ctor
test_func
Example 3, operator bool ....
1
D is deleting Foo
~Foo dtor

2.3 auto_ptr和unique_ptr对比

unique_ptr作为auto_ptr的升级版,优势具体在哪里,我们做实验来进行对比。

1.赋值运算

差异:unique_ptr比auto_ptr的赋值运算更为安全;

  • auto_ptr的赋值运算符等效于调用 reset(r.release()) ,实际上是把智能指针的所有权转移,原智能指针被release之后已经变成nullptr;
  • unique_ptr 不允许直接赋值,需要使用移动赋值

1.autoptr例子:

//1.auto_ptr的例子,直接赋值
class people
{
public:
    people() {
        cout << "people()" << endl;
    }

    people(string name) {
        this->name = name;
        cout << "people(string name)" << endl;
    }

    ~people() {
        cout << "~people()" << endl;
    }

    void print_name(void)
    {
        cout << "name: " << this->name << endl;
    }
private:
    string name;

};

void test_func(auto_ptr<people> ptr)
{
	ptr->print_name();
}

int main(void)
{
	//1.测试1:直接使用=赋值运算符,等效于调用 reset(r.release())
    auto_ptr<people> p1(new people("jason"));
	auto_ptr<people> p2 = p1;
	
	p2->print_name();
    p1->print_name(); //这里发生了core dumped,发生coredump的原因是此时p1已经是Nullptr,被reset(nullptr)了
}

测试结果:

$ ./a.out
people(string name)
name: jason
Segmentation fault (core dumped)

2.unique_ptr例子:

struct Foo {
	int val;

	Foo() {cout << "Foo default ctor\n";}
	Foo(int _val):val(_val) {cout << "Foo int" << this->val <<endl;}
	~Foo() {cout << "~Foo dtor\n";}
	void print_val() {
		cout << "val is" << this->val << endl;
	}
};

//删除器
struct D {
	void test_func(void) {
		cout << "test_func" << endl;
	};
	
	D() {cout << "D default ctor\n";}
	D(const D&) {cout << "D copy ctor\n";}
	void operator()(Foo* p) const {
		cout << "D is deleting Foo\n";
		delete p;
	};
};
int main(void)
{
	unique_ptr<Foo> up(new Foo(4));

	// unique_ptr<Foo> up2 = up; //unique_ptr不允许直接赋值,error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Foo; _Dp = std::default_delete<Foo>]’


	unique_ptr<Foo> up2 = move(up);
	up2->print_val();

	return 0;
}

测试结果:
在这里插入图片描述

2.作为函数参数的差异

差异:

  • auto_ptr作为函数参数时,只能传引用,而不能传值,传值会导致一次指针的赋值,导致指针变空;
  • unique_ptr作为函数参数时,既能传引用,又能传值;

1.autoptr例子:
测试代码:
使用 test_func 时,后面会发生coredump,使用 test_func_re()就可以,原因是因为传值作为参数时,会产生一个临时对象来接受参数,其实又回到第一个差异,赋值的问题。

class people
{
public:
    people() {
        cout << "people()" << endl;
    }

    people(string name) {
        this->name = name;
        cout << "people(string name)" << endl;
    }

    ~people() {
        cout << "~people()" << endl;
    }

    void print_name(void)
    {
        cout << "name: " << this->name << endl;
    }
private:
    string name;

};

void test_func(auto_ptr<people> ptr)
{
	ptr->print_name();
}

void test_func_re(auto_ptr<people>& ptr)
{
	ptr->print_name();
}

int main(void)
{
	//2.测试2:auto_ptr作为函数的参数
	auto_ptr<people> p1(new people("jason"));
	// test_func(p1); //在这里就发生了析构
	// p1->print_name(); //core dumped
	test_func_re(p1);
}

测试结果:
在这里插入图片描述
2.unique_ptr例子:

struct Foo {
	int val;

	Foo() {cout << "Foo default ctor\n";}
	Foo(int _val):val(_val) {cout << "Foo int" << this->val <<endl;}
	~Foo() {cout << "~Foo dtor\n";}
	void print_val() {
		cout << "val is" << this->val << endl;
	}
};

//删除器
struct D {
	void test_func(void) {
		cout << "test_func" << endl;
	};
	
	D() {cout << "D default ctor\n";}
	D(const D&) {cout << "D copy ctor\n";}
	void operator()(Foo* p) const {
		cout << "D is deleting Foo\n";
		delete p;
	};
};

void test_arg_func(unique_ptr<Foo> ptr)
{
	ptr->print_val();
}
int main(void)
{
	unique_ptr<Foo> up(new Foo(4));
	test_arg_func(move(up));

	return 0;
}

在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值