C/C 程序的设计机制:Pimpl机制

发布于:2023-06-29 20:17:10

作者:学到牛牛任金城

我们平时在编写C/C++程序时我们都会在源文件(后缀为.c或.cpp的文件)中包含头文件,当头文件内容发生改变时,包含其的源文件在编译时也需要重新编译,也就是现在假如有如下关系图:

这里的N个cpp文件都包含了A.h,当每次修改A.h内容,再次编译时这里的N个cpp文件无论内容是否改变都将重新编译!这时就好比是牵一发而动全身,会导致编译效率低下。

那么该如何解决呢?也就是如何才能做到既能兼顾头文件内容的可扩展兼容性,又能去掉那些重复无效的编译(编译那些无需再次编译的源文件即内容并未发生改变的源文件)。这就是我们今天的主角——PIMPL机制(一种利用指针实现的数据私有化可扩展兼容机制)。我们先来看一个没有引入PIMPL机制的情况,测试代码如下:

person.h

#pragma once

class Person

{

public:

void setAge(unsigned short age);

unsigned short getAge();

private:

#ifdef WIN32

uint16 mage;

// int maddTest;

#else

unsigned short mage;

#endif

};

person.cpp

#include "person.h"

void Person::setAge(unsigned short age)

{

mage = age;

}

unsigned short Person::getAge()

{

return mage;

}

man.h

pragma once

#include "person.h"

class Man : public Person

{

public:

void showInfo();

};

man.cpp

#include "man.h"

#include

void Man::showInfo()

{

std::cout << getAge()<< std::endl;

}

xuedaon.cpp

#include "man.h"

int main()

{

Man rose;

rose.setAge(18);

rose.showInfo();

return 0;

}

我们再编写一个Makefile文件来模拟IDE的编译过程

Makefile

FILES += person.o man.o xuedaon.o

INCLUDE += person.h man.h

APP = test

default:$(APP)

person.o:person.cpp $(INCLUDE)

g++-c person.cpp

man.o:man.cpp $(INCLUDE)

g++-c man.cpp

xuedaon.o:xuedaon.cpp $(INCLUDE)

g++-c xuedaon.cpp

$(APP):$(FILES)

g++$(FILES)-o $(APP)

clean:

rm -rf $(FILES)$(APP)

当我们执行make时效果如下:

此时若我们没做任何文件修改若直接再次执行make效果则会是:

当我们将头文件person.h中注释的测试成员取消注释:

后再执行make的效果:

此时我们会发现所有的.cpp源文件又都重新被编译,但他们都未做任何更改,那这些编译目前来说都是无效的没有必要的编译,这里增加的属性就好比是针对于windows环境做出的调整改变,本质对当前环境是没有任何影响的,当前环境下也不需要去关心这个变化,但是它却影响了当前环境下的编译,这样肯定是不太好的,所以我们就可以利用PIMPL机制来改进,即在原类对象中提供一个用于存储数据的私有对象指针,将类对象中原有的私有数据成员放入在源文件在定义的私有对象中还可以定义相关访问接口便于原对象调用,具体简单改进后代码如下:

person.h

#pragma once

#include

#include

class Person

{

public:

Person();

void setAge(unsigned short age);

unsigned short getAge();

private:

struct PrivatePersonData;//提供一个私有的用于存储数据的对象

std::sharedptr mpData;//利用指向私有数据对象的智能指针提供数据操作接口,在C语言中就可以直接定义为普通指针,这里需注意的是由于PrivatePersonData对象在头文件这里是没有定义的,若使用uniqueptr将无法正常创建mpData智能指针对象,具体改进方法就是先在头文件中定义一个私有对象的抽象父类,用该父类去定义智能指针即可

};

person.cpp

include "person.h"

struct Person::PrivatePersonData //在源文件中定义私有数据对象,这样该对象中的成员发生任何改变就只与当前文件有关

{

#ifdef WIN32

uint16 mage;

// int maddTest;

#else

unsigned short mage;

#endif

};

Person::Person()

{

mpData = std::sharedptr(new PrivatePersonData);//利用智能指针实例化一个私有数据对象

}

void Person::setAge(unsigned short age)

{

mpData->mage = age;

}

unsigned short Person::getAge()

{

return mpData->mage;

}

此时当我们第一次执行make时效果与之前一样会编译所有源文件:

但当我们在私有数据对象中改变时(将现在PrivatePersonData中int maddTest;注释去除)再make时的效果:

此时我们可以看出与修改后无关联的源代码(man.cpp与xuedaon.cpp)并没有再次重新编译,这样就既能兼顾头文件内容的可扩展兼容性,又能去掉那些重复无效的编译,并且还相当于对外隐藏了Person类中原有的数据成员,防止代码的盗用;在Qt中就是利用在源文件中定义私有对象配合定义QDECLAREPRIVATE(定义获取私有对象的指针的接口函数和声明私有对象)与QD宏定义(调用接口获取私有对象的指针)实现的PIPML机制,有兴趣的可以看看下面的源代码:

//使用普通类型指针时调用的接口,用于返回当前私有对象的指针

template

static inline T *getPtr(T *ptr){

return ptr;

}

//使用智能指针类对象时调用的接口,作用同上

template

static inline typename Wrapper::pointer getPtr(const Wrapper &p){

return p.get();// get()是uniqueptr中的方法,作用是获取智能管理指针

}

#define QDECLAREPRIVATE(Class)\

inline Class##Private *dfunc(){ \

return reinterpretcast(getPtr(dptr));}\

inline const Class##Private *dfunc() const {\

return reinterpretcast(getPtr(dptr));}\

friend class Class##Private

#define QD(Class) Class##Private *const dp = dfunc()

希望上面的介绍能对大家理解PIMPL机制有所帮助!祝大家在学习的道路上能更上一层楼。

作者:学到牛牛任金城,欢迎关注公众号【学到牛牛】。更多入门到精通课件、资料请参看这里:学到牛牛 www.xuedaon.com


免责声明:本站所有内容及图片均采集来源于网络,并无商业使用,如若侵权请联系删除。

上一篇:微软将“生成见解”引入Visual Studio,可智能分析开发者代码张子枫未修生图流出,被网友公开批评:她的胸碍了谁的“意淫梦”?

下一篇:Vulkan教程 -**新跨平台高效能计算机3D图形API

资讯 观察行业视觉,用专业的角度,讲出你们的心声。
MORE

I NEED TO BUILD WEBSITE

我需要建站

*请认真填写需求信息,我们会在24小时内与您取得联系。