pimpl与unique_ptr的问题

PImplstd::unique_ptr组合

pimpl(Pointer to Implementation)C++程序开发中非常常用的技巧之一,它的好处有:

  1. 节省程序编译时间
  2. 保持程序/库的二进制兼容性
  3. 隐藏实现细节

举例一个常见的pimpl的使用示例:

// a.h
class Impl;	//前置声明
class A
{
	A();
	~A();
	//...
private:
	Impl* m_pimpl = nullptr;	
};

//a.cpp
//include "impl.h"
A::A()
{
	m_pimpl = new Impl();
}

A::~A()
{
	if (m_pimpl != nulllptr)
	{
		delete m_pimpl;
		m_pimpl = nullptr;
	}
}

这是一个最简单的使用pimpl隐藏实现细节的例子,在析构函数中delete m_pimpl;确保不会内存泄漏,


在现代C++中提倡使用智能指针,我们尝试将以上的代码改为智能指针的形式:

// a.h
class Impl;	//前置声明
class A
{
	A();
	//...
private:
	std::unique_ptr<Impl> m_pimpl = nullptr;	
};

//a.cpp
//include "impl.h"
A::A()
{
	m_pimpl = std::make_unique<Impl>();
}

可以看到,我们将A类的析构函数删掉了,因为智能指针会自动帮我管理Impl申请的堆内存
但是这段代码无法通过编译,报错: error: C2338: can't delete an incomplete type;


导致这个问题产生的原因:

A类被销毁时,类A的析构函数被调用,而类A并没有显示的析构函数,根据编译器生成特殊成员函数的规则,编译器会帮我们生成一个默认的析构函数,在这个析构函数中,调用 m_pimpl成员变量的析构函数,这是一个使用默认删除器std::unique_ptr类对象,在这个类的析构函数中,会检查它所持有的类型是否为不完整类型;

由于编译器生成的特殊成员函数是隐式内敛的,它们的作用域仅在a.h头文件中,而在这个头文件中,只有Impl类的前置声明,没有Impl类的实现,所以在这些编译器默认生成的函数中,Impl是一个不完整的类型


怎样解决这个问题:

为类A手动添加析构函数,且不要在头文件中写实现体,因为Impl类的完整实现仅在a.h中不可见,在a.cpp文件中可见;

参考1
参考2

你可能感兴趣的:(C++基础,c++,学习)