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();
}