DLL导出函数Interface与实现分离

December 17, 2023
测试
测试
测试
测试
8 分钟阅读

一种最简单的实现

GPImpl.h/.cpp

class GPImpl
{
public:
	void DoSomeThing();
};
//------------------------------
void GPImpl::DoSomeThing()
{
   cout << "DoSomeThing" << endl;
}

GPExp.h/.cpp

#include "GPImpl.h"
class GP_EXPORT IGPExp 
{
public:
	void DoSomeThing();
private:
	GPImpl m_GPImpl;
};
//----------------------------------
void IGPExp::DoSomeThing()
{
	m_GPImpl.DoSomeThing();
}

这时如果我们提供了GPExp.h,那么m_GPImpl也暴露了,这时我们必须同时提供GPImpl.h,如果GPExp.h中有很多m_GPImpl这样的成员变量,那我们就得提供很多头文件了,而且只要其中任一个类有变动,我们都要给用户更新头文件,

也就是需要重新编译,对于大项目来说,这是要命的

//--------------------------------------------------------改进:

接口与实现分离

对IGPExp这个导出类写一个实现类GPExpImpl来实现它的所有功能,重点:GPExpImpl必须和IGPExp有相同的公有成员函数,因为它们一个是接口,一个是实现,要一一对应

GPImpl.h/.cpp

class GPImpl
{
public:
	void DoSomeThing();
};

/// <summary>
///		GPExp的实现类
/// </summary>
class GPExpImpl
{
public:
	void DoSomeThing();
private:
	GPImpl m_GPImpl;
};
//----------------------------------------------------
void GPImpl::DoSomeThing()
{
	cout << "DoSomeThing" << endl;
}

//====
void GPExpImpl::DoSomeThing()
{
	m_GPImpl.DoSomeThing();
}

GPExp.h/.cpp

#pragma once

class GPExpImpl;// 这样声明就不需要包含头文件
class GP_EXPORT IGPExp 
{
public:
	IGPExp();
	~IGPExp();
	void DoSomeThing();

private:
	GPExpImpl* m_pImpl;
};
//----------------------------------------------------------
#include "GPExp.h"
#include "inc\GPImpl.h"
IGPExp::IGPExp()
{
	m_pImpl = new GPExpImpl;
}

IGPExp::~IGPExp()
{
	if (m_pImpl)
	{
		delete m_pImpl;
	}
}

void IGPExp::DoSomeThing()
{
	m_pImpl->DoSomeThing();
}

划重点:前置声明class GPExpImpl;不需要包含头文件,但GPExpImpl只能使用指针,否则过不了编译

//-----------------------------------------深入,如果IGPExp有父类,父类有函数IsOk来控制是否调用DoSomeThing()

第一种方式:IGPExp::DoSomeThing()判断

GPExp.h/.cpp

class GPExpImpl;
class GP_EXPORT IGPExpBase
{
public:
	bool IsOk();
	virtual void DoSomeThing() = 0;
};


class GP_EXPORT IGPExp : public IGPExpBase
{
public:
	IGPExp();
	~IGPExp();
	void DoSomeThing();

private:
	GPExpImpl* m_pImpl;
};
//----------------------------------------------------
#include "GPExp.h"
#include "inc\GPImpl.h"

bool IGPExpBase::IsOk()
{
	return true;
}

//-------------------------------
IGPExp::IGPExp()
{
	m_pImpl = new GPExpImpl;
}

IGPExp::~IGPExp()
{
	if (m_pImpl)
	{
		delete m_pImpl;
	}
}

void IGPExp::DoSomeThing()
{
	if (IsOk())
	{
		m_pImpl->DoSomeThing();
	}
}

这样的话,接口就加入了实现细节,从而接口和实现没有彻底分离

更好的方式,把IGPExp的指针传给实现GPExpImpl

GPImpl.h/.cpp

#pragma once
class IGPExp;
class GPImpl
{
public:
	void DoSomeThing();
};

/// <summary>
///		GPExp的实现类
/// </summary>
class GPExpImpl
{
public:
	GPExpImpl(IGPExp* pExp);
	void DoSomeThing();
private:
	GPImpl m_GPImpl;
	IGPExp* m_pExp;
};
//-----------------------------------------
#include "GPImpl.h"
#include "..\\GPExp.h"
#include <iostream>
using namespace std;

void GPImpl::DoSomeThing()
{
	cout << "DoSomeThing" << endl;
}

//====
GPExpImpl::GPExpImpl(IGPExp* pExp)
{
	m_pExp = pExp;
}
void GPExpImpl::DoSomeThing()
{
	if (m_pExp->IsOk())
	{
		m_GPImpl.DoSomeThing();
	}
}

GPExp.h/.cpp

class GPExpImpl;
class GP_EXPORT IGPExpBase
{
public:
	bool IsOk();
	virtual void DoSomeThing() = 0;
};

class GP_EXPORT IGPExp : public IGPExpBase
{
public:
	IGPExp();
	~IGPExp();
	void DoSomeThing();

private:
	GPExpImpl* m_pImpl;
};
//--------------------------------------------
#include "GPExp.h"
#include "inc\GPImpl.h"

bool IGPExpBase::IsOk()
{
	return true;
}

//-------------------------------
IGPExp::IGPExp()
{
	m_pImpl = new GPExpImpl(this);
}

IGPExp::~IGPExp()
{
	if (m_pImpl)
	{
		delete m_pImpl;
	}
}

void IGPExp::DoSomeThing()
{
	m_pImpl->DoSomeThing();
}

为什么不让GPExpImpl直接继承IGPExpBase呢,因为GPExpImpl定位为IGPExp的执行体,和IGPExpBase实在没有什么关系

继续阅读

更多来自我们博客的帖子

如何安装 BuddyPress
由 测试 December 17, 2023
经过差不多一年的开发,BuddyPress 这个基于 WordPress Mu 的 SNS 插件正式版终于发布了。BuddyPress...
阅读更多
Filter如何工作
由 测试 December 17, 2023
在 web.xml...
阅读更多
如何理解CGAffineTransform
由 测试 December 17, 2023
CGAffineTransform A structure for holding an affine transformation matrix. ...
阅读更多