Boost开发指南-3.4shared_ptr

shared_ptr

shared_ptr与 scoped_ptr一样包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时才删除被包装的动态分配的对象。

shared_ptr也可以安全地放到标准容器中,是在STL容器中存储指针的最标准解法。

类摘要

template<class T>
class shared_ptr
{
public:
   typedef T element_type;  //内部类型定义
   shared_ptr() ; //构造函数
   template<class Y> explicit shared_ptr(Y * p);
   template<class Y, class D> shared_ptr(Y * p, D d);
   ~shared_ptr(); //析构函数
   shared_ptr(shared_ptr const & r); //拷贝构造
   shared_ptr & operator=(shared_ptr const & r); //赋值操作
   template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r);
   void reset(); //重置智能指针
   template<class Y> void reset(Y * p);
   template<class Y, class D> void reset(Y * p, D d) ;
   T & operator*() const; //操作符重裁
   T & operator->() const; //操作符重载
   T * get() const; //获得原始指针
   bool unique() const; //是否唯一
   long use_count() const; //引用计数
   explicit operator bool() const; //显式bool值转型
   void swap(shared_ptr & b); //交换指针
};

操作函数

shared_ptr与 scoped_ptr同样是用于管理new动态分配对象的智能指针,因此功能上有很多相似之处:它们都重载了*和->操作符以模仿原始指针的行为,提供显式 bool类型转换以判断指针的有效性,get()可以得到原始指针,并且没有提供指针算术操作,也不能管理new[]产生的动态数组指针。例如:

shared_ptr<int> spi(new int); //一个int的shared_ptr
assert(spi); //在bool语境中转换为bool的值
*spi = 253; //使用解引用操作符*

shared_ptr<std::string>  sps(new std::string("smart")); //一个string的shared_ptr
assert(sps->size() == 5); //使用箭头操作符->

//shared_ptr<int> dont_do_this(new int[10]); //危险,不能正确释放内存

但shared_ptr的名字表明了它与scoped_ptr的主要不同:它是可以被安全共享的一一shared_ptr是一个“全功能”的类,有着正常的拷贝、赋值语义,也可以进行shared_ptr间的比较,是“最智能”的智能指针。

shared_ptr有多种形式的构造函数,应用于各种可能的情形:
1)无参的shared_ptr()创建一个持有空指针的shared_ptr
2)shared_ptr(Y * p)获得指向类型r的指针p 的管理权,同时引用计数置为1。这个构造函数要求Y类型必须能够转换为T类型
3)shared_ptr(shared_ptr const & r)从另外一个shared_ptr获得指针的管理权,同时引用计数加1,结果是两个shared_ptr 共享一个指针的管理权
4)operator=赋值操作符可以从另外一个shared_ptr获得指针的管理权,其行为同构造函数
5)shared_ptr(Y * p, D d)行为类似shared_ptr(Y * p),但使用参数d指定了析构时的定制删除器,而不是简单的delete
6)别名构造函数(aliasing),不增加引用计数的特殊用法

shared_ptr的reset()函数的行为与scoped_ptr也不尽相同,它的作用是将引用计数减1,停止对指针的共享,除非引用计数为,否则不会发生删除操作。带参数的reset()则类似相同形式的构造函数,原指针引用计数减1的同时改为管理另一个指针。

shared_ptr有两个专门的函数来检查引用计数。unique()在 shared_ptr是指针的唯一所有者时返回true(这时shared_ptr的行为类似scoped_ptr或unique_ptr),use_count()返回当前指针的引用计数。要小心,use_count()应该仅仅用于测试或者调试,它不提供高效率的操作,而且有的时候可能是不可用的(极少数情形)。而unique()则是可靠的,任何时候都可用,而且比use_count()==1速度更快。

shared_ptr还支持比较运算,可以测试两个shared_ptr的相等或不相等,比较基于内部保存的指针,相当于a.get()==b.get()。shared_ptr还可以使用 operator<比较大小,同样基于内部保存的指针,但不提供除 operator<以外的比较操作符,这使得shared_ptr可以被用于标准关联容器(set和 map):

typedef shared_ptr<std::string> sp_t; //shared_ptr类型定义
std::map<sp_t, int> m; //标准映射容器

sp_t sp(new std::string("one")); //一个shared_ptr对象
m[sp] = 111; //关联数组用法

此外,shared_ptr还支持流输出操作符operator<<,输出内部的指针值,方便调试。

用法

shared_ptr的高智能使其行为最接近原始指针,因此它比 scoped_ptr的应用范围更广。几乎是100%可以在任何new出现的地方接受new的动态分配结果,然后被任意使用,从而完全消灭delete的使用和内存泄漏,而它的用法与scoped_ptr同样的简单。

shared_ptr也提供基本的线程安全保证,一个shared_ptr可以被多个线程安全读取,但其他的访问形式结果是未定义的。

示范shared_ptr基本用法的例子如下:

shared_ptr<int> sp(new int(10)); //一个指向整数的shared_ptr
assert(sp.unique()); //现在shared_ptr是指针的唯一持有者

shared_ptr<int> sp2 = sp; //第二个shared_ptr,拷贝构造函数

assert(sp == sp2 &&  //两个shared_ptr相等
       sp.use_count() == 2);  //指向同一个对象,引用计数为2

*sp2 = 100; //使用解引用操作符修改被指对象
assert(*sp == 100); //另一个shared_ptr也同时被修改

sp.reset(); //停止shared_ptr的使用
assert(!sp); //sp不再持有任何指针(空指针)

第二个例子示范了shared_ptr较复杂的用法:

class shared  //一个拥有shared_ptr的类
{
private:
	shared_ptr<int> p; //shared_ptr成员变量
public:
	shared(shared_ptr<int> p_) :p(p_) {}  //构造函数初始化shared_ptr
	void print()   //输出shared_ptr的引用计数和指向的值                        
	{
		std::cout << "count:" << p.use_count()
			<< " v=" << *p << std::endl;
	}
};

void print_func(shared_ptr<int> p) //使用shared_ptr作为函数参数
{
	std::cout << "count:" << p.use_count() //同样输出引用计数和指向的值
		<< " v=" << *p << std::endl;
}

int main()
{
	shared_ptr<int> p(new int(100));
	shared s1(p), s2(p); //构造两个自定义类

	s1.print();
	s2.print();

	*p = 20; //修改shared_ptr所指的值
	print_func(p);

	s1.print();
}

这段代码定义了一个类和一个函数,两者都接受shared_ptr对象作为参数,特别注意的是我们没有使用引用的方式传递参数,而是直接拷贝,就像是在使用一个原始指针——shared_ptr支持这样的用法。

在声明了shared_ptr和两个shared类实例后,指针被它们所共享,因此引用计数为3。print_func()函数内部拷贝了一个 shared_ptr对象,因此引用计数再增加1,但当退出函数时拷贝自动析构,引用计数又恢复为3。

工厂函数

shared_ptr很好地消除了显式的 delete 调用,如果掌握了它的用法,可以肯定delete将会在你的编程字典中彻底消失。

但这还不够,因为shared_ptr的构造还需要new调用,这导致了代码中的某种不对称性。虽然shared_ptr很好地包装了new表达式,但过多的显式new操作符也是个问题,显式delete调用应该使用工厂模式来解决。

因此,smart_ptr库提供了一个工厂函数(位于boost名字空间)make_shared(),来消除显式的new调用,声明如下:

template<class T, class... Args>   //C++11可变参数模板
typename boost::detail::sp_if_not_array<T>::type //模板元计算类型
make_shared(Args && .. args); //C++11的右值引用用法

make_shared()函数可以接受若干个参数,然后把它们传递给类型T的构造函数,创建一个shared_ptr<T>的对象并返回。通常make_shared()函数要比直接创建shared_ptr对象的方式快且高效,因为它内部仅分配一次内存,消除了shared_ptr构造时的开销。

下面的代码示范了make_shared()函数的用法:

auto sp = make_shared<std::string>("make_shared"); //创建string的共享指针
auto spv = make_shared<std::vector<int> >(10, 2); //创建vector的共享指针
assert(spv->size() == 10);

如果C++编译器支持C++11的可变参数模板特性,那么make_shared()的参数数量没有限制,能够以任意多数量的参数构造对象,否则它只能接受最多10个参数,一般情况下这不会成为问题。实际上,很少有如此多的参数的函数接口,即使有,那也会是一个设计得不够好的接口,应该被重构。

除了make_shared(),smart_ptr库还提供一个allocate_shared(),它比 make_shared()多接受一个定制的内存分配器类型参数,其他方面都相同。

应用于标准容器

有两种方式可以将 shared_ptr应用于标准容器(或者容器适配器等其他容器)。

一种用法是将容器作为shared ptr管理的对象,如 shared_ptr<list<T>>,使容器可以被安全地共享,用法与普通shared_ptr没有区别,我们不再讨论。

另一种用法是将shared_ptr 作为容器的元素,如 vector<shared_ptr<T>>,因为shared_ptr 支持拷贝语义和比较操作,符合标准容器对元素的要求,所以可以在容器中安全地容纳元素的指针而不是拷贝。

标准容器不能容纳scoped_ptr,因为scoped_ptr不能拷贝和赋值。标准容器可以容纳原始指针,但这就丧失了容器的许多好处,因为标准容器无法自动管理类型为指针的元素,必须编写额外的大量代码来保证指针最终被正确删除,这通常很麻烦而且容易出错。

存储shared_ptr的容器与存储原始指针的容器功能几乎一样,但 shared_ptr为程序员做了指针的管理工作,可以任意使用shared_ptr而不用担心资源泄漏。

下面的代码示范了将shared_ptr应用于标准容器的用法:

int main()
{
	typedef std::vector<shared_ptr<int> > vs; //一个持有shared_ptr的标准容器类型
	vs v(10); //声明一个拥有10个元素的容器
              //元素被初始化为空指针
	int i = 0;
	for (auto pos = v.begin(); pos != v.end(); ++pos)
	{
		(*pos) = make_shared<int>(++i); //使用工厂函数赋值
		std::cout << *(*pos) << ", "; //输出值
	}
	std::cout << std::endl;

	for (auto& ptr : v)
	{
		ptr = make_shared<int>(++i);
		std::cout << *ptr << ", ";
	}
	std::cout << std::endl;

	shared_ptr<int> p = v[9];
	*p = 100;
	std::cout << *v[9] << std::endl;
}

这段代码里需要注意的是迭代器和 operator[]的用法,因为容器内存储的是shared_ptr,我们必须对迭代器pos使用一次解引用操作符*以获得shared_ptr,然后再对shared_ptr使用解引用操作符*才能操作真正的值。* (*pos)也可以直接写成**pos,但前者更清晰,后者很容易让人迷惑。

vector的 operator[]用法与迭代器类似,也需要使用*获取真正的值。

使用boost.foreach库或者C++11里的for可以避免迭代器到shared_ptr的两次解引用,直接取出容器里的shared_ptr,例如:

for (auto& ptr : v)   //C++11的for循环,注意是引用形式
{
     ptr = make_shared<int>(++i);  //使用工厂函数赋值
     cout << *ptr << ",";  //输出值
}

Boost还另外在 boost.iterators库里提供了迭代器适配器indirect_iterator来简化容纳shared_ptr容器的使用。

代码示例

#include <iostream>
#include <exception>
#include <map>
#include <boost/smart_ptr.hpp>
using namespace boost;

//

void case1()
{
	shared_ptr<int> spi(new int);
	assert(spi);
	*spi = 253;

	shared_ptr<std::string>  sps(new std::string("smart"));
	assert(sps->size() == 5);

	//shared_ptr<int> dont_do_this(new int[10]);
}

//

void case2()
{
	typedef shared_ptr<std::string> sp_t;
	std::map<sp_t, int> m;

	sp_t sp(new std::string("one"));
	m[sp] = 111;

	shared_ptr<std::exception> sp1(new std::bad_exception());

	auto sp2 = dynamic_pointer_cast<std::bad_exception>(sp1);
	auto sp3 = static_pointer_cast<std::exception>(sp2);
	assert(sp3 == sp1);
}

//

void case3()
{
	shared_ptr<int> sp(new int(10));
	assert(sp.unique());

	shared_ptr<int> sp2 = sp;

	assert(sp == sp2 &&
		sp.use_count() == 2);

	*sp2 = 100;
	assert(*sp == 100);

	sp.reset();
	assert(!sp);
}

//

class shared
{
private:
	shared_ptr<int> p;
public:
	shared(shared_ptr<int> p_) :p(p_) {}
	void print()
	{
		std::cout << "count:" << p.use_count()
			<< " v=" << *p << std::endl;
	}
};

void print_func(shared_ptr<int> p)
{
	std::cout << "count:" << p.use_count()
		<< " v=" << *p << std::endl;
}

void case4()
{
	shared_ptr<int> p(new int(100));
	shared s1(p), s2(p);

	s1.print();
	s2.print();

	*p = 20;
	print_func(p);

	s1.print();
}

//

void case5()
{
	auto sp = make_shared<std::string>("make_shared");
	auto spv = make_shared<std::vector<int> >(10, 2);
	assert(spv->size() == 10);
}

//

void case6()
{
	typedef std::vector<shared_ptr<int> > vs;
	vs v(10);

	int i = 0;
	for (auto pos = v.begin(); pos != v.end(); ++pos)
	{
		(*pos) = make_shared<int>(++i);
		std::cout << *(*pos) << ", ";
	}
	std::cout << std::endl;

	for (auto& ptr : v)
	{
		ptr = make_shared<int>(++i);
		std::cout << *ptr << ", ";
	}
	std::cout << std::endl;

	shared_ptr<int> p = v[9];
	*p = 100;
	std::cout << *v[9] << std::endl;
}

int main()
{
	case1();
	case2();
	case3();
	case4();
	case5();
	case6();
}

在这里插入图片描述

应用于桥接模式

桥接模式(bridge)是一种结构型设计模式,它把类的具体实现细节对用户隐藏起来,以达到类之间的最小耦合关系。在具体编程实践中桥接模式也被称为 pimpl或者handle/body惯用法,它可以将头文件的依赖关系降到最小,减少编译时间,而且可以不使用虚函数实现多态。

scoped_ptr和 shared_ptr都可以用来实现桥接模式,但shared_ptr通常更合适,因为它支持拷贝和赋值,这在很多情况下都是有用的,比如可以配合容器工作。

首先我们声明一个类sample,它仅向外界暴露了最小的细节,真正的实现在内部类impl,sample用一个shared_ptr来保存它的指针:

class sample
{
private:
	class impl; //不完整的内部类声明
	shared_ptr<impl> p; //shared_ptr成员变量
public:
	sample(); //构造函数
	void print(); //提供给外界的接口
};

在sample的cpp中完整定义impl类和其他功能:

class sample::impl //内部类的实现
{
public:
	void print()
	{
		std::cout << "impl print" << std::endl;
	}
};

sample::sample() :p(new impl) {} //构造函数初始化shared_ptr

void sample::print() //调用pimpl实现print()
{
	p->print();
}

最后是桥接模式的使用,很简单:

sample s;
s.print();

桥接模式非常有用,它可以任意改变具体的实现而外界对此一无所知,同时也减小了源文件之间的编译依赖,使程序获得了更多的灵活性。而shared_ptr是实现它的最佳工具之一,它解决了指针的共享和引用计数问题。

应用于工厂模式

工厂模式是一种创建型设计模式,这个模式包装了new操作符的使用,使对象的创建工作集中在工厂类或者工厂函数中,从而更容易适应变化,make_shared()就是工厂模式的一个很好的例子。

在程序中编写自己的工厂类或者工厂函数时通常需要在堆上使用new动态分配一个对象,然后返回对象的指针。这种做法很不安全,因为用户很容易忘记对指针调用delete,存在资源泄漏的隐患。
使用shared_ptr 可以解决这个问题,只需要修改工厂方法的接口,不再返回一个原始指针,而是返回一个被shared_ptr包装的智能指针,这样可以很好地保护系统资源,而且会更好地控制对接口的使用。

接下来我们使用代码来解释 shared_ptr应用于工厂模式的用法,首先实现一个纯抽象基类,也就是接口类:

class abstract //接口类定义
{
public:
	virtual void f() = 0;
	virtual void g() = 0;
protected:
	virtual ~abstract() = default; //注意这里
};

注意abstract 的析构函数,被定义为保护的,意味着除了它自己和它的子类,其他任何对象都无权调用delete来删除它。

然后我们再定义abstract的实现子类:

class impl :public abstract
{
public:
	impl() = default;
	virtual ~impl() = default;
public:
	virtual void f()
	{
		std::cout << "class impl f" << std::endl;
	}
	virtual void g()
	{
		std::cout << "class impl g" << std::endl;
	}
};

随后的工厂函数返回基类的shared_ptr:

shared_ptr<abstract> create()
{ return shared_ptr<abstract>(new impl); }

这样我们就完成了全部工厂模式的实现,现在可以把这些组合起来:

void case2()
{
	auto p = create(); //工厂函数创建对象
	p->f(); //可以像普通指针一样使用
	p->g(); //不必担心资源泄露,shared_ptr会自动管理指针
}

由于基类 abstract 的析构函数是保护的,所以用户不能做出任何对指针的破坏行为,即使是用get()获得了原始指针:

abstract *q = p.get(); //正确
delete q; //错误

这段代码不能通过编译,因为无法访问abstract的保护析构函数。

但这不是绝对的,使用“粗鲁”的方法也可以在 shared_ptr 外删除对象,因为 impl的析构函数是公开的,所以:

impl *q = (impl*)(p.get()); //强制转型
delete q; //ok

这样就可以任意操作原本处于shared_ptr控制之下的原始指针了,但永远也不要这样做,因为这会使shared_ptr在析构时删除可能已经不存在的指针,引发未定义行为。

定制删除器

shared_ptr(Y * p, D d)的第一个参数是要被管理的指针,它的含义与其他构造函数的参数相同。而第二个删除器参数d则告诉shared_ptr在析构时不是使用delete来操作指针p,而要用d来操作,即把delete p换成d§。

在这里删除器d可以是一个函数对象,也可以是一个函数指针,只要它能够像函数那样被调用,使得d§成立即可。对删除器的要求是它必须可拷贝,行为必须也像delete那样,不能抛出异常。
为了配合删除器的工作,shared_ptr提供一个自由函数get_deleter(shared_ptr<T>const &p),它能够返回删除器的指针。

有了删除器的概念,我们就可以用shared_ptr实现管理任意资源。只要这种资源提供了它自己的释放操作, shared_ptr就能够保证自动释放。

假设我们有一组操作socket的函数,使用一个socket_t类:

class socket_t {};  //socket类

socket_t* open_socket() //打开socket
{
	std::cout << "open_socket" << std::endl;
	return new socket_t;
}

void close_socket(socket_t* s) //关闭socket
{
	std::cout << "close_socket" << std::endl;
}

那么,socket资源对应的释放操作就是函数close_socket(),它符合shared_ptr对删除器的定义,可以用shared_ptr这样管理socket资源:

socket_t *s = open_socket();
shared_ptr<socket_t> p(s, close_socket); //传入删除器

在这里删除器close_socket()是一个自由函数,因此只需要把函数名传递给shared_ptr就可以了。在函数名前也可以加上取地址操作符&,效果是等价的:

shared_ptr<socket_t> p(s, &close_socket); //传入删除器

这样我们就使用shared_ptr配合定制的删除器管理了socket资源。当离开作用域时,shared_ptr会自动调用close_socket()函数关闭socket,再也不会有资源遗失的担心。

再例如,对于传统的使用struct FILE的C文件操作,也可以使用shared_ptr配合定制删除器自动管理,像这样:

shared_ptr<FILE> fp(fopen ("./ 1.txt", "r"), fclose);

当离开作用域时,shared_ptr会自动调用fclose()函数关闭文件。

shared_ptr的删除器特性在处理某些特殊资源时非常有用,它使得用户可以定制、扩展shared_ptr的行为,使shared_ptr不仅仅能够管理内存资源,而是成为一个“万能”的资源管理工具。

对比std::shared_ptr

C++11标准中定义了std::shared_ptr,功能与boost::shared_ptr基本相同,完全可以等价互换。

显式bool转型

早期版本的shared_ptr的bool转型函数是隐式转换,但在C++11标准发布后为了与标准一致添加了explicit修饰,变成了显式转换。

出于兼容性的考虑,C++11标准规定在if/assert/for等逻辑判断语境下shared_ptr还是可以“隐式”转换的(比如之前的代码),但其他情形——如函数参数或者返回值——则必须显式转换,可以使用static_cast<bool>(p)p != nullptr或者!!p等形式。例如:

bool bool_test() //测试shared_ptr的bool转型
{
	auto p = make_shared<int>(776); //创建一个shared_ptr

	assert(p); //assert可以隐式转换
	if (p) //if判断可以隐式转换
	{
		std::cout << "explicit cast" << std::endl;
	}

	//return !!p;
	return static_cast<bool>(p); //返回值必须显式转换
}

指针转型函数

在编写基于虚函数的多态代码时指针的类型转换很有用,比如把一个基类指针转型为一个派生类指针或者反过来。但对于shared_ptr不能使用诸如static_cast<T*>(p.get())的形式,这将导致转型后的指针无法再被shared_ptr正确管理。

为了支持这样的用法,shared_ptr提供了类似的转型函数static_pointer_cast<T>()const_pointer_cast<T>()dynamic_pointer_cast<T>(),它们与标准的转型操作符static_cast<T>等类似,但返回的是转型后的shared_ptr。

例如,下面的代码使用dynamic_pointer_cast把一个shared_ptr<std::exception>向下转型为一个shared_ptr<bad_exception>,然后又用static_pointer_cast重新转为shared_ptr<std::exception>

shared_ptr<std::exception> sp1(new bad_exception);
auto sp2 = dynamic_pointer_cast<bad_exception>(spl);
auto sp3 = static_pointer_cast<std::exception>(sp2);
assert(sp3 == sp1);

shared_ptr

shared_ptr<void>能够存储void*型的指针,而void*型指针可以指向任意类型,因此 shared_ptr<void>就像是一个泛型的指针容器,拥有容纳任意类型的能力。

但将指针存储为 void*同时也丧失了原来的类型信息,为了在需要的时候正确使用,可以用static_pointer_cast<T>等转型函数重新转为原来的指针。但这涉及运行时动态类型转换,它会使代码不够安全,建议最好不要这样使用。

删除器的高级用法

基于shared_ptr<void>和定制删除器,shared_ptr可以有更惊人的用法。由于空指针可以是任何指针类型,因此 shared_ptr<void>还可以实现退出作用域时调用任意函数。例如:

void any_func(void* p) //一个可执行任意功能的函数
{
	std::cout << "some operate" << std::endl;
}

int main()
{
	shared_ptr<void> p(nullptr, any_func); //容纳空指针,定制删除器
} //退出作用域时将执行any_func()                                       

shared_ptr<void>存储了一个空指针,并指定了删除器是操作void*的一个函数,因此当它析构时会自动调用函数any_func(),从而执行任意我们想做的工作。

别名构造函数(aliasing)

在之前介绍的构造函数之外,shared_ptr还有一种比较特殊的构造函数,形式是:

template<class Y>
shared ptr(shared_ptr<Y> const & r, element_type * p);

它的作用是共享r的引用计数,但实际持有的却是另外一个可能毫无关系的指针p,而且并不负责p的自动销毁。

初看上去这种形式的构造函数非常的怪异,但它是有实际应用价值的,一个例子使用场景是指向已经被shared_ptr管理的对象的内部成员变量:

auto p1 = make_shared<std::pair<int, int>>(0, 1); //一个pair智能指针

shared_ptr<int> p2(p1, &p1->second); //别名构造

assert(p1.use_count() == 2 &&  //原引用计数增加
	p1.use_count() == p2.use_count());  //两者引用计数相同
	
assert((void*)p1.get() != (void*)p2.get()); //但指向的内容不同
assert(&p1->second == p2.get()); //指向的是另外的指针

owner_less

因为存在别名构造函数,所以某些情况下单纯地基于p.get()的指针值比较就不适用了,为此 smart_ptr库提供了基于所有权的比较函数对象owner_less,定义了严格的弱序关系,可以用于关联容器。

下面的代码简单地示范了owner_less用于标准容器set:

#include <boost/smart_ptr/owner_less.hpp> //需要独包含头文件
void case7()
{
	typedef shared_ptr<int> int_ptr; //共享指针typedef
	typedef owner_less<int_ptr> int_ptr_less; //函数对象typedef

	int_ptr p1(new int(10)); //共享指针
	int n = 20;
	int_ptr p2(p1, &n); //别名构造

	assert(!int_ptr_less()(p1, p2) && //两者即不小于
		!int_ptr_less()(p2, p1)); //也不大于,即等价

	typedef std::set<int_ptr> int_set; //关联容器typedef

	int_set s; 
	s.insert(p1); //插入两个元素
	s.insert(p2); //因为等价所以不会被插入
	assert(s.size() == 1); //实际容器里只有一个元素
}

代码示例

#include <iostream>
#include <set>
#include <boost/core/ignore_unused.hpp>
#include <boost/smart_ptr.hpp>
using namespace boost;

//

class sample
{
private:
	class impl;
	shared_ptr<impl> p;
public:
	sample();
	void print();
};

class sample::impl
{
public:
	void print()
	{
		std::cout << "impl print" << std::endl;
	}
};

sample::sample() :p(new impl) {}

void sample::print()
{
	p->print();
}

void case1()
{
	sample s;
	s.print();
}

//

class abstract
{
public:
	virtual void f() = 0;
	virtual void g() = 0;
protected:
	virtual ~abstract() = default;
};

class impl :public abstract
{
public:
	impl() = default;
	virtual ~impl() = default;
public:
	virtual void f()
	{
		std::cout << "class impl f" << std::endl;
	}
	virtual void g()
	{
		std::cout << "class impl g" << std::endl;
	}
};

shared_ptr<abstract> create()
//{   return shared_ptr<abstract>(new impl);}
{
	return make_shared<impl>();
}

void case2()
{
	auto p = create();
	p->f();
	p->g();

	abstract* q = p.get();
	boost::ignore_unused(q);
	//delete q;
}

//

class socket_t {};

socket_t* open_socket()
{
	std::cout << "open_socket" << std::endl;
	return new socket_t;
}

void close_socket(socket_t* s)
{
	std::cout << "close_socket" << std::endl;
}

void case3()
{
	socket_t* s = open_socket();
	shared_ptr<socket_t> p(s, close_socket);
	//shared_ptr<socket_t> p(s, &close_socket);
}

//

bool case4()
{
	auto p = make_shared<int>(776);

	assert(p);
	if (p)
	{
		std::cout << "explicit cast" << std::endl;
	}

	//return !!p;
	return static_cast<bool>(p);
}

//

void any_func(void* p)
{
	std::cout << "some operate" << std::endl;
}

void case5()
{
	shared_ptr<void> p(nullptr, any_func);
}

//

void case6()
{
	auto p1 = make_shared<std::pair<int, int>>(0, 1);

	shared_ptr<int> p2(p1, &p1->second);

	assert(p1.use_count() == 2 &&
		p1.use_count() == p2.use_count());
	assert((void*)p1.get() != (void*)p2.get());
	assert(&p1->second == p2.get());
}

//
#include <boost/smart_ptr/owner_less.hpp>

void case7()
{
	typedef shared_ptr<int> int_ptr;
	typedef owner_less<int_ptr> int_ptr_less;

	int_ptr p1(new int(10));
	int n = 20;
	int_ptr p2(p1, &n);

	assert(!int_ptr_less()(p1, p2) &&
		!int_ptr_less()(p2, p1));

	typedef std::set<int_ptr> int_set;

	int_set s;
	s.insert(p1);
	s.insert(p2);
	assert(s.size() == 1);
}

int main()
{
	case1();
	case2();
	case3();
	case4();
	case5();
	case6();
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阳光开朗男孩

你的鼓励是我最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值