Location>code7788 >text

Smart pointer related: common applications of enable_shared_from_this in development

Popularity:172 ℃/2024-08-19 17:02:52

Problems with shared_ptr() in classes

When we first define a pointer and then construct two smart pointers from that pointer

int main()
{
  int* pt = new int();
  std::shared_ptr<int> p1(pt);
  std::shared_ptr<int> p2(pt);
  std::cout << "p1.use_count() = " << p1.use_count() << std::endl;
  std::cout << "p2.use_count() = " << p2.use_count() << std::endl;
  return 0;
}

When you run it, it reports an error, showing that the pt pointer was released repeatedly
image
The reason is that both p1 and p2 think they are the only smart pointers to pt and do not know that there are smart pointers to pt.
So the output reveals that both reference counts are 1

If you need to not report errors, you have to write it like this
shared_ptr<int> p2 = p1
By defining p2 by p1, they know that pt has two smart pointers and won't report errors.

Look at another code

class client
{
public:
	~client()
	{
		std::cout << "~client()\n";
	}
};

int main()
{
	client* cp = new client();
	std::shared_ptr<client> csp1(cp);
	std::shared_ptr<client> csp2(cp);

	std::cout << "csp1.use_count: " << csp1.use_count() << std::endl;
	std::cout << "csp2.use_count: " << csp2.use_count() << std::endl;

	return 0;
}

This reported the same error, the principle is the same, the problem is that our actual development, many times you need to this pointer to get the contents of the object
This time you need to solve the problem by enabling_shared_from_this

Usage of enable_shared_from_this

class client : public std::enable_shared_from_this<client>
{
public:
	~client()
	{
		std::cout << "~client()\n";
	}

	std::shared_ptr<client> get_ptr()
	{
		return shared_from_this();
	}
};

int main()
{
	client* cp = new client();
	std::shared_ptr<client> csp1(cp);
	std::shared_ptr<client> csp2 = cp->get_ptr();

	std::cout << "csp1.use_count: " << csp1.use_count() << std::endl;
	std::cout << "csp2.use_count: " << csp2.use_count() << std::endl;

	return 0;
}

Rewrite the code to look like this, with public inheritance of this template class first.
Note here that when you return a shared_ptr of a class via shared_from_this(), the object must already be managed by a shared_ptr, so you can't directlycsp2 = cp->get_ptr()You have to have it before that.csp1(cp)
In this case, shared_from_this() makes it possible to obtain a shared_ptr for the object at will as long as the reference count is not zero, and as long as there is a shared_ptr holding it, it will not die.

Application in real development, as an example of a server demo

First look at the following piece of code

struct client : std::enable_shared_from_this<client>
{
public:
	void start()
	{
		
	}
	//...Other functions
}

void start()
{
	std::shared_ptr<client> s = std::make_shared<client>();
	s->start();
}

int main()
{
	start();
	return 0;
}

Here a client's shared_ptr is initialized with make_shared. make_shared will allow objects and control blocks to be safely stored in contiguous blocks of memory. It simplifies memory management and improves performance. But writing your own deleters is not supported.
start is an initial function, in which the business will be added later, below we write a timer.

public:
	void start()
	{
		start_up();
	}
	
private:
	void start_up()
	{
		_timer.expires_from_now(std::chrono::seconds(10));
		_timer.async_wait(std::bind(&client::time_out, shared_from_this()));
	}

	void time_out()
	{
		start_up();
	}
private:
	boost::asio::steady_timer _timer;

Inside the class the timer is designed in such a way that when start() is called, the start_up() function is called to set a timer and the callback function time_out() is registered.
At this point, the start() function call is over, and the smart pointer to the temporary variable s has been released. However, the timer returns a shared_ptr of the object managed by s to the callback time_out() in async_wait by calling shared_from_this(), and the object managed by s doesn't die until the runtime callback The s-managed object does not die until the callback time_out() is run, but if the callback continues to call start_up() to reset the timer, a shared_ptr() of the object is returned to the newly registered callback time_out(), and so on, as long as the timer is not turned off, it will never die.

Based on this, it can be paired with read and write to flexibly control under what conditions the current class is kept alive and what conditions it is destructed.