Analysis of C++ PImpl implementation mechanism and code implementation

  in the introduction to beyond the c + + standard library boost library, there is a paragraph:

scoped_ptr and Pimpl usage

  scoped_ptr can be well used for many previously used bare pointers or auto_ptr, such as when implementing pimpl usage. [4] The idea behind pimpl usage is to isolate the client from all knowledge about the private part of the class. Since the client depends on the class header file, any change in the header file will affect the client, even if it is only a modification to the private section or protected section. pimpl usage hides these details by putting private data and functions into a separate class and saving them in an implementation file, then making a forward declaration of the class in the header file and saving a pointer to the implementation class. The class constructor allocates the pimpl class, and the destructor releases it. This eliminates the correlation between the header file and the implementation details. Let's construct a class that implements pimpl usage, and then use smart pointers to make it more secure.

   pimpl, also known as Cheshire Cat usage. For more instructions on pimpl usage, see www.gotw.ca/gotw/024.htm and Exceptional C + +.

   PImpl, namely Private Implementation, is generally referred to as the privatization implementation of classes. PImpl has two main functions;

  • (1) It decouples the declaration and implementation. During project development, if many files refer to the header file, and if the header file changes some implementations, all referenced files have to be recompiled during compilation, which increases the compilation time of c + + programs. If the implementation is declared with a pointer and the implemented code is changed, it will only be associated once, The coupling degree is reduced, which can reduce the compilation time.

  • (2) Pimpl, also known as pointer to implementation, can be used to decouple the "class interface and implementation". For example, during sdk development, this technique can be used to avoid exposing private details in the header file and keep the sdk interface completely separated from the implementation.

  the source code of Qt is almost all the implementation mechanism of PImpl. In order to better understand the implementation of Qt, the PImpl mechanism is illustrated by the example that the boss asked the programmer to develop software. The code is as follows:

Boss.h

#pragma once

class WorkerPrivate;

class Boss
{
public:
	Boss();
	~Boss();

	void develop_software();

private:
	WorkerPrivate* worker_private_ = nullptr;
};

Boss.cpp

#include "Boss.h"
#include <iostream>
#include "WorkerPrivate.h"

using namespace std;

Boss::Boss()
{
	worker_private_ = new WorkerPrivate();
}

Boss::~Boss()
{
	if (worker_private_)
	{
		delete worker_private_;
		worker_private_ = nullptr;
	}
}

void Boss::develop_software()
{
	cout << "I'm the boss. I have money. I can't write code, but I can ask me to write code and develop software for me" << endl;
	worker_private_->banzhuan();
}

Imitate the privatization of Qt, and the Private class ends with Private.

WorkerPrivate.h

#pragma once

class WorkerPrivate
{
public:
	WorkerPrivate() {}

	void banzhuan();
};

WorkerPrivate.cpp

#include "WorkerPrivate.h"
#include <iostream>

using namespace std;

void WorkerPrivate::banzhuan()
{
	cout << "I'm a programmer. My boss gives me money. I help my boss make software, which is also called moving bricks" << endl;
}

main test

#include <iostream>
#include "Boss.h"

int main()
{
    Boss boss;
    boss.develop_software();

    return 0;
}

   in the boss class, a programmer object pointer is privatized. Of course, it can be privatized. The boss will not develop software, but he has money and can recruit programmers to develop software. The programmer pointer is the implementation of privatization. If you want to change the software content, modify the programmer's banzhou implementation. This implementation process looks perfect, but there may be problems. Beyond the c + + standard library - Introduction to boost library says:

  it looks perfect, but it's not. This implementation is not abnormally safe! The reason is that the Boss constructor may throw an exception after the WorkerPrivate is constructed. Throwing an exception in the constructor means that the constructed object does not exist, so its destructor will not be called when the stack is expanded. This means that it is assigned to impl_ The memory of the pointer will leak. However, there is one simple solution: use scoped_ptr to rescue

  Boost uses this example to lead to its smart pointer scoped_ptr, in fact, uses the unique of std_ PTR is also OK, but it is not denied that Boost is a very excellent C + + library. Many open source libraries use Boost, such as websocket. Now use scoped_ptr to transform Boss class, as follows:

Boss.h

  add boost scoped_ptr header file, worker_private_ Declare as smart pointer

#pragma once

#include "boost/scoped_ptr.hpp" / / your computer must have the boost library installed

class WorkerPrivate;

class Boss
{
public:
	Boss();
	~Boss();

	void develop_software();

private:
    
    //Using the boost smart pointer
	boost::scoped_ptr<WorkerPrivate> worker_private_;
};

Boss.cpp

  comment out the constructed and destructed code and initialize the list worker_private_ initialization

#include "Boss.h"
#include <iostream>
#include "WorkerPrivate.h"

using namespace std;

Boss::Boss():
	worker_private_(new WorkerPrivate())
{
	//worker_private_ = new WorkerPrivate();
}

Boss::~Boss()
{
	/*if (worker_private_)
	{
		delete worker_private_;
		worker_private_ = nullptr;
	}*/
}

void Boss::develop_software()
{
	cout << "I'm the boss. I have money. I can't write code, but I can ask me to write code and develop software for me" << endl;
	worker_private_->banzhuan();
}

  destructor of WorkerPrivate class

WorkerPrivate::~WorkerPrivate()
{
	cout << "Deconstruction, ~WorkerPrivate, The code is finished. Rest and test tomorrow" << endl;
}

The output is as follows:

I'm the boss. I have money. I can't write code, but I can ask me to help me write code and develop software. I'm a programmer. The boss gives me money. I help the boss make software, which is also called brick moving analysis,
~WorkerPrivate, the code is finished, rest and test tomorrow

   it can be seen that after the end of main, the objects managed by the smart pointer are automatically destructed. It is indeed more convenient to use the smart pointer. As the Boost library says, it is safer (in fact, I don't know why it is safe. Don't ask why, it says that it is safe).

   the above is a simple implementation of c++ impl mechanism. The next article introduces the Impl implementation mechanism of Qt.

Keywords: C++ Qt

Added by frog on Mon, 20 Sep 2021 11:43:10 +0300