C++11后的智能指针-创新互联

文章目录
  • 一、智能指针unique_ptr
    • 1.基本用法
    • 2.更多技巧
  • 二、智能指针shared_ptr
    • 1.基本用法
    • 2.更多细节
    • 3.智能指针的删除器
    • 4.weak_ptr

创新互联一直秉承“诚信做人,踏实做事”的原则,不欺瞒客户,是我们最起码的底线! 以服务为基础,以质量求生存,以技术求发展,成交一个客户多一个朋友!为您提供网站制作、成都做网站、成都网页设计、微信小程序开发、成都网站开发、成都网站制作、成都软件开发、成都App制作是成都本地专业的网站建设和网站设计公司,等你一起来见证!一、智能指针unique_ptr

unique_ptr独享它指向的对象,也就是说,同时只有一个unique_ptr指向同一个对象,当这个unique_ptr被销毁时,指向的对象也随即被销毁。

包含头文件:#includetemplate>class unique_ptr
{public:
	explicit unique_ptr(pointer p) noexcept;	// 不可用于转换函数。
	~unique_ptr() noexcept;    
	T& operator*() const;            // 重载*操作符。
	T* operator->() const noexcept;  // 重载->操作符。
	unique_ptr(const unique_ptr &) = delete;   // 禁用拷贝构造函数。
	unique_ptr& operator=(const unique_ptr &) = delete;  // 禁用赋值函数。
	unique_ptr(unique_ptr &&) noexcept;	  // 右值引用。
	unique_ptr& operator=(unique_ptr &&) noexcept;  // 右值引用。
	// ...
private:
	pointer ptr;  // 内置的指针。
};

第一个模板参数T:指针指向的数据类型。
第二个模板参数D:指定删除器,缺省用delete释放资源。

1.基本用法
  • eg:测试类AA的定义:
class AA
{public:
	string m_name;
	AA() {cout<< m_name<< "调用构造函数AA()。\n"; }
	AA(const string & name) : m_name(name) {cout<< "调用构造函数AA("<< m_name<< ")。\n"; }
	~AA() {cout<< m_name<< "调用了析构函数~AA("<< m_name<< ")。\n"; }
};

1)初始化

方法一:
unique_ptrp0(new AA("西施"));     // 分配内存并初始化。

方法二:
unique_ptrp0 = make_unique("西施");   // C++14标准。
unique_ptrpp1=make_unique();         // 数据类型为int。
unique_ptrpp2 = make_unique();       // 数据类型为AA,默认构造函数。
unique_ptrpp3 = make_unique("西施");  // 数据类型为AA,一个参数的构造函数。
unique_ptrpp4 = make_unique("西施",8); // 数据类型为AA,两个参数的构造函数。

方法三(不推荐):
AA* p = new AA("西施");
unique_ptrp0(p);                  // 用已存在的地址初始化。

2)使用方法

  • 智能指针重载了*和->操作符,可以像使用指针一样使用unique_ptr。
  • 不支持普通的拷贝和赋值。
AA* p = new AA("西施");
	unique_ptrpu2 = p;              // 错误,不能把普通指针直接赋给智能指针。
	unique_ptrpu3 = new AA("西施"); // 错误,不能把普通指针直接赋给智能指针。
	unique_ptrpu2 = pu1;           // 错误,不能用其它unique_ptr拷贝构造。
	unique_ptrpu3;
	pu3 = pu1;                            // 错误,不能用=对unique_ptr进行赋值。
  • 不要用同一个裸指针初始化多个unique_ptr对象。

  • get()方法返回裸指针。

  • 不要用unique_ptr管理不是new分配的内存。

  • 用于函数的参数
    传引用(不能传值,因为unique_ptr没有拷贝构造函数)。
    裸指针。

4)不支持指针的运算(+、-、++、–)

2.更多技巧

1)将一个unique_ptr赋给另一个时,如果源unique_ptr是一个临时右值,编译器允许这样做;如果源unique_ptr将存在一段时间,编译器禁止这样做。一般用于函数的返回值。

unique_ptrfunc()
{return unique_ptr(new int{100});	
}

unique_ptrUnPtr1 = func();//OK
unique_ptrUnPtr2;
UnPtr2=func();//OK

unique_ptrp0;
p0 = unique_ptr(new AA ("西瓜"));

2)用nullptr给unique_ptr赋值将释放对象,空的unique_ptr==nullptr。

3)release()释放对原始指针的控制权,将unique_ptr置为空,返回裸指针。(可用于把unique_ptr传递给子函数,子函数将负责释放对象)

4)std::move()可以转移对原始指针的控制权。(可用于把unique_ptr传递给子函数,子函数形参也是unique_ptr)

5)reset()释放对象。

void reset(T * _ptr= (T *) nullptr);
pp.reset();        // 释放pp对象指向的资源对象。
pp.reset(nullptr);  // 释放pp对象指向的资源对象
pp.reset(new AA("bbb"));  // 释放pp指向的资源对象,同时指向新的对象。

6)swap()交换两个unique_ptr的控制权。

void swap(unique_ptr&_Right);

7)unique_ptr也可象普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样。

8)unique_ptr不是绝对安全,如果程序中调用exit()退出,全局的unique_ptr可以自动释放,但局部的unique_ptr无法释放。

unique_ptrptrGlobal = make_unique(100);//可以释放

int main()
{unique_ptrptrlocal = make_unique(100);//无法释放
	exit(0);
}

9)unique_ptr提供了支持数组的具体化版本。

// unique_ptrparr1(new int[3]);          // 不指定初始值。
unique_ptrparr1(new int[3]{33,22,11 });  // 指定初始值。
cout<< "parr1[0]="<< parr1[0]<< endl;
cout<< "parr1[1]="<< parr1[1]<< endl;
cout<< "parr1[2]="<< parr1[2]<< endl;

unique_ptrparr2(new AA[3]{string("西施"), string("冰冰"), string("幂幂")});
cout<< "parr2[0].m_name="<< parr2[0].m_name<< endl;
cout<< "parr2[1].m_name="<< parr2[1].m_name<< endl;
cout<< "parr2[2].m_name="<< parr2[2].m_name<< endl;
  • eg1:
#include#includeusing  namespace std;

class AA
{public:
	string m_name;
	AA() {cout<< m_name<< "调用构造函数AA()。\n"; }
	AA(const string & name) : m_name(name) {cout<< "调用构造函数AA("<< m_name<< ")。\n"; }
	~AA() {cout<< "调用了析构函数~AA("<< m_name<< ")。\n"; }
};

// 函数func1()需要一个指针,但不对这个指针负责。
void func1(const AA* a) {cout<< a->m_name<< endl;
}

// 函数func2()需要一个指针,并且会对这个指针负责。
void func2(AA* a) {cout<< a->m_name<< endl;
	delete a;
}

// 函数func3()需要一个unique_ptr,不会对这个unique_ptr负责。
void func3(const unique_ptr&a) {cout<< a->m_name<< endl;
}

// 函数func4()需要一个unique_ptr,并且会对这个unique_ptr负责。
void func4(unique_ptra) {cout<< a->m_name<< endl;
}

int main()
{unique_ptrpu(new AA("西施"));

	cout<< "开始调用函数。\n";
	//func1(pu.get());        // 函数func1()需要一个指针,但不对这个指针负责。
	//func2(pu.release());  // 函数func2()需要一个指针,并且会对这个指针负责。
	//func3(pu);                // 函数func3()需要一个unique_ptr,不会对这个unique_ptr负责。
	func4(move(pu));     // 函数func4()需要一个unique_ptr,并且会对这个unique_ptr负责。
	cout<< "调用函数完成。\n";

	if (pu == nullptr) cout<< "pu是空指针。\n";
}
  • eg2:
#include#includeusing  namespace std;

class AA
{public:
	string m_name;
	AA() {cout<< m_name<< "调用构造函数AA()。\n"; }
	AA(const string & name) : m_name(name) {cout<< "调用构造函数AA("<< m_name<< ")。\n"; }
	~AA() {cout<< "调用了析构函数~AA("<< m_name<< ")。\n"; }
};

int main()
{//AA* parr1 = new AA[2];   // 普通指针数组。
	AA* parr1 = new AA[2]{ string("西施"), string("冰冰") };
	//parr1[0].m_name = "西施1";
	//cout<< "parr1[0].m_name="<< parr1[0].m_name<< endl;
	//parr1[1].m_name = "西施2";
	//cout<< "parr1[1].m_name="<< parr1[1].m_name<< endl;
	//delete [] parr1;
	
	unique_ptrparr2(new AA[2]);   // unique_ptr数组。
	//unique_ptr parr2(new AA[2]{ string("西施"), string("冰冰") });
	parr2[0].m_name = "西施1";
	cout<< "parr2[0].m_name="<< parr2[0].m_name<< endl;
	parr2[1].m_name = "西施2";
	cout<< "parr2[1].m_name="<< parr2[1].m_name<< endl;
}
二、智能指针shared_ptr 1.基本用法

shared_ptr的构造函数也是explicit,但是,没有删除拷贝构造函数和赋值函数。

1)初始化

方法一:
shared_ptrp0(new AA("西施"));     // 分配内存并初始化。

方法二:
shared_ptrp0 = make_shared("西施");  // C++11标准,效率更高。
shared_ptrpp1=make_shared();         // 数据类型为int。
shared_ptrpp2 = make_shared();       // 数据类型为AA,默认构造函数。
shared_ptrpp3 = make_shared("西施");  // 数据类型为AA,一个参数的构造函数。
shared_ptrpp4 = make_shared("西施",8); // 数据类型为AA,两个参数的构造函数。

方法三:
AA* p = new AA("西施");
shared_ptrp0(p);                  // 用已存在的地址初始化。

方法四:
shared_ptrp0(new AA("西施")); 
shared_ptrp1(p0);                 // 用已存在的shared_ptr初始化,计数加1。
shared_ptrp1=p0;                 // 用已存在的shared_ptr初始化,计数加1。

2)使用方法

  • 智能指针重载了*和->操作符,可以像使用指针一样使用shared_ptr。
  • use_count()方法返回引用计数器的值。
  • unique()方法,如果use_count()为1,返回true,否则返回false。
  • shared_ptr支持赋值,左值的shared_ptr的计数器将减1,右值shared_ptr的计算器将加1。
  • get()方法返回裸指针。
  • 不要用同一个裸指针初始化多个shared_ptr。
  • 不要用shared_ptr管理不是new分配的内存。

3)用于函数的参数

  • 与unique_ptr的原理相同。

4)不支持指针的运算(+、-、++、–)

2.更多细节

1)用nullptr给shared_ptr赋值将把计数减1,如果计数为0,将释放对象,空的shared_ptr==nullptr。

2)std::move()可以转移对原始指针的控制权。还可以将unique_ptr转移成shared_ptr。(反之不可以)

3)reset()改变与资源的关联关系。

pp.reset();        // 解除与资源的关系,资源的引用计数减1。
pp. reset(new AA("bbb"));  // 解除与资源的关系,资源的引用计数减1。关联新资源。

4)swap()交换两个shared_ptr的控制权。

void swap(shared_ptr&_Right);

5)shared_ptr也可象普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样。

6)shared_ptr不是绝对安全,如果程序中调用exit()退出,全局的shared_ptr可以自动释放,但局部的shared_ptr无法释放。

7)shared_ptr提供了支持数组的具体化版本。

8)shared_ptr的线程安全性:

  • shared_ptr的引用计数本身是线程安全(引用计数是原子操作)。
  • 多个线程同时读同一个shared_ptr对象是线程安全的。
  • 如果是多个线程对同一个shared_ptr对象进行读和写,则需要加锁。
  • 多线程读写shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,也需要加锁保护。

9)如果unique_ptr能解决问题,就不要用shared_ptr。unique_ptr的效率更高,占用的资源更少。

  • eg:
#include#includeusing  namespace std;

class AA
{public:
	string m_name;
	AA() {cout<< m_name<< "调用构造函数AA()。\n"; }
	AA(const string & name) : m_name(name) {cout<< "调用构造函数AA("<< m_name<< ")。\n"; }
	~AA() {cout<< "调用了析构函数~AA("<< m_name<< ")。\n"; }
};

int main()
{shared_ptrpa0(new AA("西施a"));     // 初始化资源西施a。
	shared_ptrpa1 = pa0;                       // 用已存在的shared_ptr拷贝构造,计数加1。
	shared_ptrpa2 = pa0;                       // 用已存在的shared_ptr拷贝构造,计数加1。
	cout<< "pa0.use_count()="<< pa0.use_count()<< endl;   // 值为3。

	shared_ptrpb0(new AA("西施b"));    // 初始化资源西施b。
	shared_ptrpb1 = pb0;                      // 用已存在的shared_ptr拷贝构造,计数加1。
	cout<< "pb0.use_count()="<< pb0.use_count()<< endl;   // 值为2。

	pb1 = pa1;      // 资源西施a的引用加1,资源西施b的引用减1。
	pb0 = pa1;      // 资源西施a的引用加1,资源西施b的引用成了0,将被释放。

	cout<< "pa0.use_count()="<< pa0.use_count()<< endl;   // 值为5。
	cout<< "pb0.use_count()="<< pb0.use_count()<< endl;   // 值为5。
}
3.智能指针的删除器

在默认情况下,智能指针过期的时候,用delete原始指针; 释放它管理的资源。
程序员可以自定义删除器,改变智能指针释放资源的行为。
删除器可以是全局函数、仿函数和Lambda表达式,形参为原始指针。

  • eg:
#include#includeusing  namespace std;

class AA
{public:
	string m_name;
	AA() {cout<< m_name<< "调用构造函数AA()。\n"; }
	AA(const string & name) : m_name(name) {cout<< "调用构造函数AA("<< m_name<< ")。\n"; }
	~AA() {cout<< "调用了析构函数~AA("<< m_name<< ")。\n"; }
};

void deletefunc(AA* a) {// 删除器,普通函数。
	cout<< "自定义删除器(全局函数)。\n";
	delete a;
}

struct deleteclass               // 删除器,仿函数。
{void operator()(AA* a) {cout<< "自定义删除器(仿函数)。\n";
		delete a;
	}
};

auto deleterlamb = [](AA* a) {// 删除器,Lambda表达式。
	cout<< "自定义删除器(Lambda)。\n";
	delete a;
};

int main()
{shared_ptrpa1(new AA("西施a"), deletefunc);
	//shared_ptr pa2(new AA("西施b"), deleteclass());
	//shared_ptr pa3(new AA("西施c"), deleterlamb);
	
	//unique_ptr pu1(new AA("西施1"), deletefunc);
    // unique_ptr pu0(new AA("西施1"), deletefunc);
	//unique_ptr pu2(new AA("西施2"), deleteclass());
	//unique_ptr pu3(new AA("西施3"), deleterlamb);
}
4.weak_ptr

shared_ptr内部维护了一个共享的引用计数器,多个shared_ptr可以指向同一个资源。
如果出现了循环引用的情况,引用计数永远无法归0,资源不会被释放。

  • eg:
#include#includeusing  namespace std;

class BB;

class AA
{public:
	string m_name;
	AA() {cout<< m_name<< "调用构造函数AA()。\n"; }
	AA(const string & name) : m_name(name) {cout<< "调用构造函数AA("<< m_name<< ")。\n"; }
	~AA() {cout<< "调用了析构函数~AA("<< m_name<< ")。\n"; }
	shared_ptrm_p;
};

class BB
{public:
	string m_name;
	BB() {cout<< m_name<< "调用构造函数BB()。\n"; }
	BB(const string& name) : m_name(name) {cout<< "调用构造函数BB("<< m_name<< ")。\n"; }
	~BB() {cout<< "调用了析构函数~BB("<< m_name<< ")。\n"; }
	shared_ptrm_p;
};

int main()
{shared_ptrpa = make_shared("西施a");
	shared_ptrpb = make_shared("西施b");
	
	pa->m_p = pb;
	pb->m_p = pa;
}

weak_ptr 是为了配合shared_ptr而引入的,它指向一个由shared_ptr管理的资源但不影响资源的生命周期。

也就是说,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。

不论是否有weak_ptr指向,如果最后一个指向资源的shared_ptr被销毁,资源就会被释放。

weak_ptr更像是shared_ptr的助手而不是智能指针。

  • eg:
#include#includeusing  namespace std;

class BB;

class AA
{public:
	string m_name;
	AA() {cout<< m_name<< "调用构造函数AA()。\n"; }
	AA(const string & name) : m_name(name) {cout<< "调用构造函数AA("<< m_name<< ")。\n"; }
	~AA() {cout<< "调用了析构函数~AA("<< m_name<< ")。\n"; }
	weak_ptrm_p;
};

class BB
{public:
	string m_name;
	BB() {cout<< m_name<< "调用构造函数BB()。\n"; }
	BB(const string& name) : m_name(name) {cout<< "调用构造函数BB("<< m_name<< ")。\n"; }
	~BB() {cout<< "调用了析构函数~BB("<< m_name<< ")。\n"; }
	weak_ptrm_p;
};

int main()
{shared_ptrpa = make_shared("西施a");
	shared_ptrpb = make_shared("西施b");
	
	cout<< "pa.use_count()="<< pa.use_count()<< endl;
	cout<< "pb.use_count()="<< pb.use_count()<< endl;

	pa->m_p = pb;
	pb->m_p = pa;

	cout<< "pa.use_count()="<< pa.use_count()<< endl;
	cout<< "pb.use_count()="<< pb.use_count()<< endl;
}

如何使用weak_ptr ?
weak_ptr没有重载 ->和 *操作符,不能直接访问资源。
有以下成员函数:

1)operator=();  // 把shared_ptr或weak_ptr赋值给weak_ptr。
2)expired();     // 判断它指资源是否已过期(已经被销毁)。
3)lock();        // 返回shared_ptr,如果资源已过期,返回空的shared_ptr。
4)reset();       // 将当前weak_ptr指针置为空。
5)swap();       // 交换。

weak_ptr不控制对象的生命周期,但是,它知道对象是否还活着。
用lock()函数把它可以提升为shared_ptr,如果对象还活着,返回有效的shared_ptr,如果对象已经死了,提升会失败,返回一个空的shared_ptr。
提升的行为(lock())是线程安全的。

  • eg:
#include#includeusing  namespace std;

class BB;

class AA
{public:
	string m_name;
	AA() {cout<< m_name<< "调用构造函数AA()。\n"; }
	AA(const string& name) : m_name(name) {cout<< "调用构造函数AA("<< m_name<< ")。\n"; }
	~AA() {cout<< "调用了析构函数~AA("<< m_name<< ")。\n"; }
	weak_ptrm_p;
};

class BB
{public:
	string m_name;
	BB() {cout<< m_name<< "调用构造函数BB()。\n"; }
	BB(const string& name) : m_name(name) {cout<< "调用构造函数BB("<< m_name<< ")。\n"; }
	~BB() {cout<< "调用了析构函数~BB("<< m_name<< ")。\n"; }
	weak_ptrm_p;
};

int main()
{shared_ptrpa = make_shared("西施a");

	{shared_ptrpb = make_shared("西施b");

		pa->m_p = pb;
		pb->m_p = pa;

		shared_ptrpp = pa->m_p.lock();            // 把weak_ptr提升为shared_ptr。
		if (pp == nullptr)
			cout<< "语句块内部:pa->m_p已过期。\n";
		else
			cout<< "语句块内部:pp->m_name="<< pp->m_name<< endl;
	}

	shared_ptrpp = pa->m_p.lock();            // 把weak_ptr提升为shared_ptr。
	if (pp == nullptr)
		cout<< "语句块外部:pa->m_p已过期。\n";
	else
		cout<< "语句块外部:pp->m_name="<< pp->m_name<< endl;
}
  • ref:链接

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


网站名称:C++11后的智能指针-创新互联
链接URL:http://myzitong.com/article/djcssg.html