Boost Development Guide - 3.4 shared_ptr

shared_ptr

Shared_ptr, like scoped_ptr, wraps the dynamic object allocated by the new operator on the heap, but it implements a reference-counted smart pointer, which can be freely copied and assigned, and shared anywhere, when no code is used (reference count is 0) it deletes the wrapped dynamically allocated object.

shared_ptr can also be safely placed in standard containers and is the most standard solution for storing pointers in STL containers.

class summary

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); //交换指针
};

operation function

Shared_ptr and scoped_ptr are also smart pointers used to manage new dynamically allocated objects, so there are many similarities in function: they both overload *和->operators to imitate the behavior of raw pointers, and provide explicit bool type conversions to determine the validity of pointers Sex, get() can get the original pointer, and does not provide pointer arithmetic operations, nor can it manage the dynamic array pointer generated by new[]. For example:

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]); //危险,不能正确释放内存

But the name of shared_ptr indicates the main difference between it and scoped_ptr: it can be safely shared—shared_ptr is a "full-featured" class with normal copy and assignment semantics, and can also perform comparisons between shared_ptr, which is " The smartest "smart pointer.

shared_ptr has multiple forms of constructors, which are used in various possible situations:
1) Shared_ptr() with no parameters creates a shared_ptr holding a null pointer
2) shared_ptr(Y * p)Obtains the management right of the pointer p pointing to type r, and at the same time sets the reference count to is 1. This constructor requires that the Y type must be able to be converted to the T type
3) shared_ptr(shared_ptr const & r)Obtain the management right of the pointer from another shared_ptr, and at the same time increase the reference count by 1, the result is that two shared_ptr share the management right of a pointer
4) The operator= assignment operator can be obtained from Another shared_ptr obtains the management right of the pointer, and its behavior is similar to that of the constructor
5) , but the parameter d is used to specify the custom deleter at the time of destruction instead of simple delete 6) Aliasing the constructor (aliasing), without adding references Special Uses for Countingshared_ptr(Y * p, D d)shared_ptr(Y * p)

The behavior of the reset() function of shared_ptr is also different from that of scoped_ptr. Its function is to decrement the reference count by 1 and stop sharing the pointer. Unless the reference count is , the deletion operation will not occur. The reset() with parameters is similar to the constructor of the same form, and the reference count of the original pointer is reduced by 1 while managing another pointer.

shared_ptr has two dedicated functions to check the reference count. unique() returns true when shared_ptr is the sole owner of the pointer (then shared_ptr behaves like scoped_ptr or unique_ptr), and use_count() returns the reference count of the current pointer. Be careful, use_count() should only be used for testing or debugging, it does not provide efficient operation, and sometimes it may not be available (rare cases). Unique() is reliable, available at any time, and faster than use_count()==1.

shared_ptr also supports comparison operations, which can test the equality or inequality of two shared_ptrs. The comparison is based on the internally saved pointer, which is equivalent to a.get()==b.get(). shared_ptr can also use operator< to compare the size, also based on the internally stored pointer, but does not provide comparison operators other than operator<, which makes shared_ptr can be used for standard associative containers (set and 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; //关联数组用法

In addition, shared_ptr also supports the stream output operator operator<<, which outputs internal pointer values ​​for easy debugging.

usage

Shared_ptr's high intelligence makes its behavior the closest to raw pointers, so it has a wider range of applications than scoped_ptr. It is almost 100% possible to accept the dynamic allocation result of new in any place where new appears, and then be used arbitrarily, thereby completely eliminating the use of delete and memory leaks, and its usage is as simple as scoped_ptr.

shared_ptr also provides basic thread safety guarantees, a shared_ptr can be safely read by multiple threads, but the results of other access forms are undefined.

An example demonstrating the basic usage of shared_ptr is as follows:

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不再持有任何指针(空指针)

The second example demonstrates a more complex usage of 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();
}

This code defines a class and a function, both of which accept shared_ptr objects as parameters. Pay special attention to the fact that we do not use references to pass parameters, but copy them directly, just like using a raw pointer - shared_ptr support such usage.

After declaring shared_ptr and two shared class instances, the pointer is shared by them, so the reference count is 3. The print_func() function internally copies a shared_ptr object, so the reference count increases by 1, but when the function is exited, the copy is automatically destructed, and the reference count returns to 3.

factory function

shared_ptr does a good job of eliminating explicit delete calls. If you master its usage, you can be sure that delete will completely disappear in your programming dictionary.

But that's not enough, because the construction of shared_ptr also requires a new call, which leads to some kind of asymmetry in the code. Although shared_ptr wraps the new expression well, too many explicit new operators are also a problem, and explicit delete calls should be solved using the factory pattern.

Therefore, the smart_ptr library provides a factory function (located in the boost namespace) make_shared() to eliminate explicit new calls, declared as follows:

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

The make_shared() function can accept several parameters, and then pass them to the constructor of type T, create an shared_ptr<T>object and return it. Usually the make_shared() function is faster and more efficient than directly creating shared_ptr objects, because it only allocates memory once internally, eliminating the overhead of shared_ptr construction.

The following code demonstrates the use of the make_shared() function:

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

If the C++ compiler supports the variable parameter template feature of C++11, then there is no limit to the number of parameters of make_shared(), and objects can be constructed with any number of parameters, otherwise it can only accept up to 10 parameters, which in general won't be a problem. In fact, there are very few functional interfaces with so many parameters, and even if there were, it would be a poorly designed interface that should be refactored.

In addition to make_shared(), the smart_ptr library also provides allocate_shared(), which accepts one more custom memory allocator type parameter than make_shared(), and is the same in other respects.

for standard containers

There are two ways to apply shared_ptr to standard containers (or other containers such as container adapters).

One usage is to use the container as an object managed by shared ptr, for example shared_ptr<list<T>>, so that the container can be safely shared. The usage is no different from that of ordinary shared_ptr, and we will not discuss it again.

Another usage is to use shared_ptr as the element of the container. For example vector<shared_ptr<T>>, because shared_ptr supports copy semantics and comparison operations, which meets the requirements of standard containers for elements, it can safely hold pointers to elements in the container instead of copying.

Standard containers cannot hold scoped_ptr, because scoped_ptr cannot be copied and assigned. Standard containers can hold raw pointers, but this loses many of the benefits of containers, because standard containers cannot automatically manage elements of type pointer, and an extra large amount of code must be written to ensure that pointers are eventually properly deleted, which is often cumbersome and error-prone .

The container storing shared_ptr has almost the same function as the container storing raw pointers, but shared_ptr manages pointers for programmers, and shared_ptr can be used arbitrarily without worrying about resource leaks.

The following code demonstrates the use of shared_ptr for standard containers:

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

What needs to be noted in this code is the usage of iterator and operator[], because shared_ptr is stored in the container, we must use the dereference operator on the iterator pos once to obtain shared_ptr, and then use the dereference operator on *shared_ptr *In order to operate the real value. * (*pos)It can also be written directly **pos, but the former is clearer, and the latter is easily confusing.

The usage of operator[] of vector is similar to that of iterators, and it also needs to be used *to get the real value.

Using the boost.foreach library or for in C++11 can avoid double dereferences from the iterator to shared_ptr, and directly take out the shared_ptr in the container, for example:

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

Boost also provides an iterator adapter indirect_iterator in the boost.iterators library to simplify the use of shared_ptr containers.

code example

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

insert image description here

Applied in bridge mode

Bridge mode (bridge) is a structural design mode that hides the specific implementation details of classes from users to achieve the minimum coupling relationship between classes. In specific programming practice, the bridge mode is also called pimpl or handle/body idiom, which can minimize the dependencies of header files, reduce compilation time, and realize polymorphism without using virtual functions.

Both scoped_ptr and shared_ptr can be used to implement the bridge mode, but shared_ptr is usually more suitable because it supports copying and assignment, which is useful in many cases, such as working with containers.

First, we declare a class sample, which only exposes the smallest details to the outside world. The real implementation is in the internal class impl, and sample uses a shared_ptr to save its pointer:

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

The impl class and other functions are fully defined in the sample's cpp:

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

Finally, the use of bridge mode is very simple:

sample s;
s.print();

Bridge mode is very useful. It can change the specific implementation arbitrarily without the outside world knowing about it. It also reduces the compilation dependencies between source files and makes the program more flexible. And shared_ptr is one of the best tools to achieve it, it solves the problem of pointer sharing and reference counting.

Applied to the factory pattern

The factory mode is a creational design mode. This mode wraps the use of the new operator and concentrates the creation of objects in the factory class or factory function, making it easier to adapt to changes. make_shared() is a good example of the factory mode. example of.

When writing your own factory class or factory function in a program, you usually need to use new to dynamically allocate an object on the heap, and then return a pointer to the object. This approach is very unsafe, because users can easily forget to call delete on the pointer, and there is a potential resource leak.
Using shared_ptr can solve this problem, only need to modify the interface of the factory method, no longer return a raw pointer, but return a smart pointer wrapped by shared_ptr, which can protect system resources well, and will better control the interface usage of.

Next, we use the code to explain the usage of shared_ptr in the factory mode. First, implement a pure abstract base class, which is the interface class:

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

Note that abstract's destructor is defined as protected, meaning that no object other than itself and its subclasses has the right to call delete to delete it.

Then we define the implementation subclass of 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;
	}
};

The subsequent factory function returns the shared_ptr of the base class:

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

In this way, we have completed the implementation of all factory patterns, and now we can combine these:

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

Since the destructor of the base class abstract is protected, the user cannot do any damage to the pointer, even if the original pointer is obtained with get():

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

This code will not compile because the protected destructor of abstract cannot be accessed.

But this is not absolute, using the "rude" method can also delete objects outside shared_ptr, because the destructor of impl is public, so:

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

In this way, the original pointer that was originally under the control of shared_ptr can be manipulated arbitrarily, but never do this, because it will cause shared_ptr to delete the pointer that may no longer exist when it is destructed, causing undefined behavior.

custom deleter

shared_ptr(Y * p, D d)The first argument to is the pointer to be managed, which has the same meaning as the other constructor arguments. The second deleter parameter d tells shared_ptr not to use delete to operate the pointer p during destructuring, but to use d to operate, that is, replace delete p with d§.

Here, the deleter d can be a function object or a function pointer, as long as it can be called like a function so that d§ holds. The requirement for a deleter is that it must be copyable, behave like delete, and must not throw exceptions.
To work with the deleter, shared_ptr provides a free function get_deleter(shared_ptr<T>const &p)that returns a pointer to the deleter.

With the concept of deleter, we can use shared_ptr to manage any resource. A shared_ptr is guaranteed to be automatically released as long as the resource provides its own release operation.

Suppose we have a set of functions for manipulating sockets, using a socket_t class:

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

Then, the release operation corresponding to the socket resource is the function close_socket(), which conforms to the definition of the deleter by shared_ptr, and the socket resource can be managed by shared_ptr like this:

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

Here the deleter close_socket() is a free function, so you only need to pass the function name to shared_ptr. The address operator & can also be added before the function name, the effect is equivalent:

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

In this way, we use shared_ptr with a custom deleter to manage socket resources. When leaving the scope, shared_ptr will automatically call the close_socket() function to close the socket, so there is no longer any worry about resource loss.

For another example, for traditional C file operations using struct FILE, shared_ptr can also be used to automatically manage with a custom deleter, like this:

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

When leaving the scope, shared_ptr will automatically call the fclose() function to close the file.

The deleter feature of shared_ptr is very useful when dealing with some special resources. It allows users to customize and extend the behavior of shared_ptr, making shared_ptr not only manage memory resources, but become a "universal" resource management tool.

Contrasted with std::shared_ptr

std::shared_ptr is defined in the C++11 standard, and its function is basically the same as that of boost::shared_ptr, and it can be completely interchanged.

Explicit bool cast

The bool conversion function of the early version of shared_ptr is an implicit conversion, but after the release of the C++11 standard, an explicit modification was added in order to be consistent with the standard, and it became an explicit conversion.

For the sake of compatibility, the C++11 standard stipulates that shared_ptr can still be "implicitly" converted in logical judgment contexts such as if/assert/for (such as the previous code), but in other cases—such as function parameters or Return value - must be explicitly converted, you can use the form of static_cast<bool>(p), p != nullptror, !!petc. For example:

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); //返回值必须显式转换
}

pointer conversion function

Pointer type conversion is useful when writing polymorphic code based on virtual functions, such as converting a base class pointer to a derived class pointer or vice versa. But for shared_ptr, the form such as cannot be used static_cast<T*>(p.get()), which will cause the transformed pointer to no longer be correctly managed by shared_ptr.

In order to support such usage, shared_ptr provides similar transformation functions static_pointer_cast<T>(), const_pointer_cast<T>()and dynamic_pointer_cast<T>()they are similar to standard transformation operators static_cast<T>, but return the transformed shared_ptr.

For example, the following code uses dynamic_pointer_cast to shared_ptr<std::exception>downcast one to one shared_ptr<bad_exception>, and then uses static_pointer_cast back to 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>It can store void*type pointers, and void*type pointers can point to any type, so shared_ptr<void>it is like a generic pointer container, which has the ability to accommodate any type.

But storing the pointer as void*also loses the original type information at the same time. In order to use it correctly when needed, you can use the static_pointer_cast<T>transformation function to convert it to the original pointer again. But this involves dynamic type conversion at runtime, which makes the code less safe, and it is recommended not to use it like this.

Advanced usage of deleters

Based on shared_ptr<void>and custom deleters, shared_ptr can have even more amazing uses. Since a null pointer can be of any pointer type, it shared_ptr<void>is also possible to call an arbitrary function on exiting the scope. For example:

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>A null pointer is stored, and the deleter is specified as void*a function of the operation, so when it is destructed, the function any_func() will be automatically called to perform any work we want.

Aliasing constructors

In addition to the constructors introduced earlier, shared_ptr also has a special constructor in the form:

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

Its role is to share the reference count of r, but it actually holds another pointer p that may have nothing to do with it, and it is not responsible for the automatic destruction of p.

At first glance, this form of constructor is very weird, but it has practical application value. An example usage scenario is to point to an internal member variable of an object already managed by 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

Because of the existence of alias constructors, in some cases, purely based p.get()pointer value comparison is not applicable. For this reason, the smart_ptr library provides an ownership-based comparison function object owner_less, which defines a strict weak order relationship and can be used for associative containers. .

The following code simply demonstrates owner_less for a standard container 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); //实际容器里只有一个元素
}

code example

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

insert image description here

Guess you like

Origin blog.csdn.net/qq_36314864/article/details/131889856