c++到com组件过度(一个实例)

com组件 专栏收录该内容
1 篇文章 0 订阅

com的核心体系结构来源于c++标准实现


c++的重用机制有一个严重的缺陷:要想试用该源对象就必须提供源代码,这样的话宝贵的技术会随之暴露。要解决这个问题在windows下可以使用DLL(dynamic link library 动态链接库),即将数据库对象的所有代码都打包成一个DLL,并提供一个或多个用于说明函数和结构的头文件。

动态链接库的使用原理:编译链接可执行文件时,只需要链接引入库DLL中的函数代码和数据并不复制到可执行文件中,在运行的时候,再去加载DLL访问DLL中导出的函数,这种方式称为动态链接。

首先学会创建动态库:(vc下创建)

1.在vc下新建一个动态库文件


2.选择动态库文件,工程名


3.选择第三个,它会自动生成统一的.cpp和.h文件


4.这样我们的动态库文件就建立完毕了


打开以后就是这个样子


将使用的函数声明继续往类中添加

函数实现放在.cpp中


如果在编译时出现这样的错误时:


请按照下方操作:



这是关于预处理的问题。。。。了解详情请问度娘^_^

动态库编译成功的标志:当当当




要将对象的实现封装成DLL,必须实现成员函数的引出,而引出成员函数最简单的方法是__declspec(dllexport)例如:

__declspec(dllexport)用于任何函数包括类的成员函数



应用程序要使用动态链接库可采用隐式和显式两种方法

隐式链接:

1.客户需将使用.dll .lib .h 文件添加到该工程文件夹下

2.在该工程的Project->Setting->Link->Object/library modules中加入xxxx.lib


显式链接过于复杂不在赘述

---------------------------------------------------------------------------------------------------------------

前面所创建的一个简单例子在这里说明一下:

  • 某库厂商开发一个算法,能在常时间内进行子串的查找工作。特点是其查找时间与目的串的长度无关。 该厂商创建了一个字符串类。为此,他生成了一个头文件和一个实现文件

// The following ifdef block is the standard way of creating macros which make exporting 
// from a DLL simpler. All files within this DLL are compiled with the FASTSTRING_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see 
// FASTSTRING_API functions as being imported from a DLL, wheras this DLL sees symbols
// defined with this macro as being exported.
#ifdef FASTSTRING_EXPORTS
#define FASTSTRING_API __declspec(dllexport)
#else
#define FASTSTRING_API __declspec(dllimport)
#endif

// This class is exported from the Faststring.dll
class FASTSTRING_API CFaststring {
	char * m_psz;
public:
	CFaststring(void);
	// TODO: add your methods here.
	CFaststring (const char*psz);
	~CFaststring ();
	int Length(void)const;
	char* Find(const char*psz)const;
};

extern FASTSTRING_API int nFaststring;

FASTSTRING_API int fnFaststring(void);

// Faststring.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "Faststring.h"
#include
    
     

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}


// This is an example of an exported variable
FASTSTRING_API int nFaststring=0;

// This is an example of an exported function.
FASTSTRING_API int fnFaststring(void)
{
	return 42;
}

// This is the constructor of a class that has been exported.
// see Faststring.h for the class definition
CFaststring::CFaststring()
{ 
	return; 
}
CFaststring::CFaststring(const char* psz):m_psz(new char[strlen(psz)+1])
{strcpy(m_psz,psz);}  //分配内存

CFaststring::~CFaststring(void)
{ delete []m_psz;}    //释放内存

int CFaststring::Length(void) const{
	return strlen(m_psz);}  //计算长度

char* CFaststring::Find(const char*psz)const
{
	return strstr(m_psz,psz);
} //查找子串


    

提到动态链接就不得不提静态链接:
  • 静态链接
    • 许多类库的做法
    • 编译时刻的链接
  • 静态链接的缺点
    • 代码重复:多个程序各有自己的代码,需要更多的内存
    • 客户程序占据更多的外存空间
    • 库代码更新需要重新编译所有的客户程序
  • 动态链接
    • 运行时刻的链接
  • 动态链接形式
    • 编译时刻通过引入库
    • 运行时刻完全动态


---------------------------------------------------------------------------------------------------------------


  • 改造FastString,令其Length()函数的复杂度为O(1)。 
  • 解决方法:需要添加一个私有成员来直接计数字符串长度


// The following ifdef block is the standard way of creating macros which make exporting 
// from a DLL simpler. All files within this DLL are compiled with the FASTSTRING2_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see 
// FASTSTRING2_API functions as being imported from a DLL, wheras this DLL sees symbols
// defined with this macro as being exported.
#ifdef FASTSTRING2_EXPORTS
#define FASTSTRING2_API __declspec(dllexport)
#else
#define FASTSTRING2_API __declspec(dllimport)
#endif

// This class is exported from the FastString2.dll
class FASTSTRING2_API CFastString2 {
public:
	CFastString2(void);
	// TODO: add your methods here.
	int m_cch;//v2.0 为了支持这个新的算法, 必须增加一个成员变量,以存储字符串的长度. 在构造函数中被赋值, 以后,当客户查询长度时,直接返回,无需计算. 导致了头文件的更新.
	char * m_psz;

	CFastString2 (const char*psz);
	~CFastString2 ();
	int Length(void)const;
	char* Find(const char*psz)const;

};

extern FASTSTRING2_API int nFastString2;

FASTSTRING2_API int fnFastString2(void);

//
// FastString2.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "FastString2.h"

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}


// This is an example of an exported variable
FASTSTRING2_API int nFastString2=0;

// This is an example of an exported function.
FASTSTRING2_API int fnFastString2(void)
{
	return 42;
}

// This is the constructor of a class that has been exported.
// see FastString2.h for the class definition
CFastString2::CFastString2()
{ 
	return; 
}

CFastString2::CFastString2(const char* psz)
:m_cch(strlen(psz)), m_psz(new char[m_cch+1])
{strcpy(m_psz,psz);}//长度保存下来

CFastString2::~CFastString2(void)
{ delete []m_psz;}

int CFastString2::Length(void) const{
	return m_cch;}// 直接返回, 无需计算, 提高了效率.

char* CFastString2::Find(const char*psz)const
{	return strstr(m_psz,psz);}// 查找子串



由于我们将类重新定义造成,新老客户使用DLL文件会不兼容配置混 问题的根源在于C++的编译模型,这种模型不能支持独立的二进制组件的设计。客户必须准确地知道对象的布局结构,从而导致客户和库的强的耦合关系。客户与库的紧密耦合性使得在不重新编译客户的情况下,无法实现类的替换。
明白的说就是:用户很开心升级了软件,刚开始还可以运行后来就弹出了一个对话框发生了异常,并且用户的所有工作都丢失了,当他试着再启动程序时,这个对话框又弹了出来。。。。。发生这个原因大概就是,上一个版本类定义给客户只分配了4个字节并传给了构造函数,升级以后的版本传给构造和析构的却是8个字节,并且要对这8个字节毫无保留的写入,不幸的是后4个字节属于别人,你这样非法操作,操作系统就把你终止掉喽(╥﹏╥)

---------------------------------------------------------------------------------------------------------------


解决方案:接口与实现分离
构造两个类一个接口类,一个实现类
  • 接口类只描述希望客户知道的底层数据面貌
  • 接口类不应包含任何用于对象实现的数据成员,应只包含操作方法的声明。
  • 实现类将包含具体的数据成员与操作方法的具体实现。
  • 接口类的二进制布局不会随着实现类数据成员的添加或删除而改变

// The following ifdef block is the standard way of creating macros which make exporting 
// from a DLL simpler. All files within this DLL are compiled with the FASTSTRINGITF_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see 
// FASTSTRINGITF_API functions as being imported from a DLL, wheras this DLL sees symbols
// defined with this macro as being exported.
#ifdef FASTSTRINGITF_EXPORTS
#define FASTSTRINGITF_API __declspec(dllexport)
#else
#define FASTSTRINGITF_API __declspec(dllimport)
#endif

// This class is exported from the FastStringItf.dll
class FastString;  // 声明实现类, 但是不需要包含实现类的头文件
class FASTSTRINGITF_API CFastStringItf {
public:
	CFastStringItf(void);
	// TODO: add your methods here.
	
	FastString* m_pThis;  //实现类的指针.

    CFastStringItf(const char*psz);
     ~CFastStringItf();
     int Length(void)const;
     char* Find(const char*psz) const;

};

extern FASTSTRINGITF_API int nFastStringItf;

FASTSTRINGITF_API int fnFastStringItf(void);

class FastString{
	char * m_psz;  //保存原始的字符串
	int m_cch;
public:
	FastString(const char*psz);   //构造函数
	~FastString(void);
	int Length(void)const;          //返回该字符串的长度
	char* Find(const char*psz)const;     //查找指定的子串
};
//
#include "FastString.h"
#include "string.h"

FastString::FastString(const char* psz):m_cch(strlen(psz)),m_psz(new char[m_cch+1])
{strcpy(m_psz,psz);}//长度保存下来

FastString::~FastString(void)
{ delete []m_psz;}

int FastString::Length(void) const{
	return m_cch;}// 直接返回, 无需计算, 提高了效率.

char* FastString::Find(const char*psz)const
{	return strstr(m_psz,psz);}// 查找子串

///
// FastStringItf.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "FastStringItf.h"
#include "FastString.h"

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}


// This is an example of an exported variable
FASTSTRINGITF_API int nFastStringItf=0;

// This is an example of an exported function.
FASTSTRINGITF_API int fnFastStringItf(void)
{
	return 42;
}

// This is the constructor of a class that has been exported.
// see FastStringItf.h for the class definition
CFastStringItf::CFastStringItf()
{ 
	return; 
}

CFastStringItf::CFastStringItf(const char *psz):m_pThis(new FastString(psz))//构造函数里面生成了实现类的具体事例通过这个指针指向实现类
{}
//{assert(m_pThis!=0);}//判断实现类是否为空

 CFastStringItf::~CFastStringItf(void){
         delete m_pThis;}//析构函数最后调用
   
int CFastStringItf::Length(void) const{
          return  m_pThis->Length();}//传递到实现类的lengh

char* CFastStringItf::Find(const char*psz)const{
          return m_pThis->Find(psz);}


其实这个改进的本意是:使上面出现的bug不再出现,用一个句丙类作为接口,并不暴露源码也就是FastString类的构造,应该用 #include "FastString.h" 包含头文件的方法来访问,并不是直接把两个头文件的内容都写在一起。。。好吧我承认是我在偷懒,,,刚开始是分开放的但是有很多报错然后就。。。


采用句柄类把接口与实现相分离,使得用户与对象之间形成一道精密的协议,用户只有通过接口类才能实现与对象之间的通信,它不依赖于实现类的任何细节。

但这种方法也有缺点:接口类必须把每个方法调用显式的传递给实现类,简单的类接口少,复杂的就会很麻烦。所以这个方法并没有完全解决这个问题。

---------------------------------------------------------------------------------------------------------------


通过将C++对象封装到DLL中,客户需重用对象只需链接DLL并包含相应的头文件和.lib文件即可,客户所见即接口文件中类的声明,从而使实现代码有效保护起来。但c++方法还具有这样的问题,对象的实现细节都包含在类的声明中,对于对象的私有成员客户不可以访问但仍然可以看的一清二楚。实际客户希望从对象那里获得的只是成员函数的地址。所以可建立一个只包含所有引用成员函数地址指针的表,客户调用函数时,只需简单的查找这个表活的函数的地址然后跳转到该地址执行即可。这里,c++抽象基类可以很好的解决这一问题,因为编译器回味抽象类建立一个包含所有成员函数的函数表。(这里不清楚的同学可以戳下面的链接Y(^_^)Y记得点赞哦!)
c++继承和多态:
http://blog.csdn.net/cherry_ermao/article/details/51105450

再回到这个例子上,通过抽象类创建的IFaststring类:


// The following ifdef block is the standard way of creating macros which make exporting 
// from a DLL simpler. All files within this DLL are compiled with the IFASTSTRING1_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see 
// IFASTSTRING1_API functions as being imported from a DLL, wheras this DLL sees symbols
// defined with this macro as being exported.
#ifdef IFASTSTRING1_EXPORTS
#define IFASTSTRING1_API __declspec(dllexport)
#else
#define IFASTSTRING1_API __declspec(dllimport)
#endif

// This class is exported from the IFastString1.dll
class IFASTSTRING1_API CIFastString1 {
public:
//	CIFastString1(void);
	// TODO: add your methods here.
	virtual int Length(void) const=0;
	virtual char* Find(const char*psz)const=0;
};


class FastString : public CIFastString1  //派生于接口类 
{
	const int m_cch;
	char*m_psz;
public:
	FastString(const char* psz);
	~FastString();
	int Length(void)const;  //不需要加上virtual 关键字
	char* Find(const char*psz)const;//覆盖了基类的虚函数
}; 

extern "C"{      __declspec(dllexport) 	
             CIFastString1 *CreateFastString(const char*psz);}//导出函数为接口类的指针
extern IFASTSTRING1_API int nIFastString1;

IFASTSTRING1_API int fnIFastString1(void);

///
// IFastString1.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "IFastString1.h"

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}


// This is an example of an exported variable
IFASTSTRING1_API int nIFastString1=0;

// This is an example of an exported function.
IFASTSTRING1_API int fnIFastString1(void)
{
	return 42;
}

// This is the constructor of a class that has been exported.
// see IFastString1.h for the class definition
// CIFastString1::CIFastString1()
// { 
// 	return; 
// }

CIFastString1 * CreateFastString(const char * psz)
{return new FastString(psz);}  //创建一个实现类的实例. 返回值是接口指针.通过基类指针才可以调用派生类函数

FastString::FastString(const char* psz)
:m_cch(strlen(psz)), m_psz(new char[m_cch+1])
{strcpy(m_psz,psz);}


FastString::~FastString(void)
{ delete []m_psz;}   // 释放成员变量占用的内存.

int FastString::Length(void) const{
	return m_cch;}

char* FastString::Find(const char*psz)const
{
	return strstr(m_psz,psz);
}






---------------------------------------------------------------------------------------------------------------


  • 用一个基类指针pIFS指向一个子类对象。delete pIFS;将导致基类的析构函数被调用.而实现类(派生类)中所动态分配的保存字符串的内存将不能得到及时释放.造成内存泄漏问题
  • 简单的解决方法1:
  • 把基类的析构函数声明为虚的.这样,派生类的析构函数也是虚的.执行delete pIFS;,由于pIFS指向的是派生类的对象,所以调用的是派生类的析构函数.可以完整地释放派生类对象所占用的存储空间。但是如果将接口类的析构函数做成虚的,会破坏接口类的编译器独立性。因为虚析构函数在虚表中的位置随着编译器的不同而不同。
  • 解决方法2
  • 给接口类增加一个Delete方法,作为其另一个纯虚函数,在派生类中实现自身的删除,以导致正确的析构过程。

// The following ifdef block is the standard way of creating macros which make exporting 
// from a DLL simpler. All files within this DLL are compiled with the IFASTSTRINGD_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see 
// IFASTSTRINGD_API functions as being imported from a DLL, wheras this DLL sees symbols
// defined with this macro as being exported.
#ifdef IFASTSTRINGD_EXPORTS
#define IFASTSTRINGD_API __declspec(dllexport)
#else
#define IFASTSTRINGD_API __declspec(dllimport)
#endif

// This class is exported from the IFastStringD.dll
class IFASTSTRINGD_API CIFastStringD {
public:
	//CIFastStringD(void);
	// TODO: add your methods here.
    virtual void Delete(void)=0;
	virtual int Length(void) const=0;//没有实现的虚函数,典型的纯虚函数
	virtual char* Find(const char*psz)const=0;

};


class FastString : public CIFastStringD  //派生于接口类
{
//	const int m_cch;
	char*m_psz;
public:
	FastString(const char* psz);
	~FastString();
	void Delete(void);
	int Length(void)const;  //不需要加上virtual 关键字
	char* Find(const char*psz)const;//覆盖了基类的虚函数
};

extern "C"{      __declspec(dllexport) 	
             CIFastStringD *CreateFastString(const char*psz);}//导出函数返回值为接口类的指针
extern IFASTSTRINGD_API int nIFastStringD;

IFASTSTRINGD_API int fnIFastStringD(void);


// IFastStringD.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "IFastStringD.h"
#include
   
    

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}


// This is an example of an exported variable
IFASTSTRINGD_API int nIFastStringD=0;

// This is an example of an exported function.
IFASTSTRINGD_API int fnIFastStringD(void)
{
	return 42;
}

// This is the constructor of a class that has been exported.
// see IFastStringD.h for the class definition
// CIFastStringD::CIFastStringD()
// { 
// 	return; 
// }

CIFastStringD * CreateFastString(const char * psz)
{return new FastString(psz);}  //创建一个实现类的实例. 返回值是接口指针.


void FastString::Delete()
{ delete this; }   //由用户调用,删除自身.

FastString::~FastString(void)
{ delete []m_psz;}   // 由上一个函数引起,释放成员变量占用的内存.
 //构造函数, Length Find 等不变,略.
/
// FastString::FastString(const int ch):m_cch(ch)
// { 
//  	return; 
// }
FastString::FastString(const char* psz):m_psz(new char[strlen(psz)+1])
{strcpy(m_psz,psz);}  //分配内存

int FastString::Length(void) const{
	return strlen(m_psz);}  //计算长度

char* FastString::Find(const char*psz)const
{
	return strstr(m_psz,psz);
} //查找子串

   



从C++过度到com需要经历的过程

实现接口的引用计数

对象允许多个接口

类厂对象使用标准IClassFactory接口

使用_stdcall调用约定

实现DLL动态卸载

实现对象自注册


声明:文中所提及的源码都是在动态链接库下实现的,所以会有些看起来奇怪的宏注释之类的,还有命名可能会有些奇怪,我发现所有构建好的类动态库会自动在其前面加上一个C,例如CFaststring,如果你在VC下按照上面的步骤同样建立动态链接库的话,会看到这些代码是如何出来的。。。。本文章仅供参考,如发现问题欢迎留言骚扰( ̄▽ ̄)


  • 1
    点赞
  • 2
    评论
  • 3
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值