深圳企业网站制作哪个,wordpress 增加注册页面,广丰区建设局网站,最好看的免费观看视频Pimpl(Pointer to implementation#xff0c;指向实现的指针) 是一种减少代码依赖和编译时间的C编程技巧#xff0c;其基本思想是将一个外部可见类(visible class)的实现细节#xff08;一般是所有私有的非虚成员#xff09;放在一个单独的实现类(implementation class)中指向实现的指针) 是一种减少代码依赖和编译时间的C编程技巧其基本思想是将一个外部可见类(visible class)的实现细节一般是所有私有的非虚成员放在一个单独的实现类(implementation class)中而在可见类中通过一个私有指针来间接访问该实现类。
C虽然不太常提到设计模式但是对外接口和实现细节的分离仍然是必须的。c是静态编译语言他看的就是文件和文件之间的依赖如果是实例 type a那么就一定需要include type相关头文件这样导致一件事情当多重依赖的时候很可能基层类的小改动导致所有包括这个类的大类都需要重新编译
减少编译时间和代码依赖
.h文件中定义了一个类虽然类中只有一些对外暴露的接口的成员函数但是类中包含了一些private的成员变量。虽然不影响使用但是从规范上讲是不合理的。因此需要将接口和实现的细节进行分离。也就是常说的信息隐藏。 对外Release的一个头文件a.h
class A
{
public:X getX();Y getY();Z getZ();private:X god;Y damn;Z it;
};头文件形式如下private成员变量
#include X.h
#include Y.h
#include Z.hclass A
{
public:X getX();Y getY();Z getZ();private:X god;Y damn;Z it;
};如果直接使用private的方式进行信息隐藏面临多个问题
别人能看到private成员变量的信息必须同时给出依赖的X.hY.h和Z.h依赖的头文件和类本身的任何改动都将引发重新编译即使这个改动本质上是不影响外部调用的。这种方式本质上是一种紧耦合只是简单的面向对象的封装隐藏实现细节。
使用依赖类的声明而非定义这种方式的头文件形式如下
class X;
class Y;
class Z;class A
{
public:X getX();Y getY();Z getZ();private:X god;Y damn;Z it;
};可以看到不用再包含X.hY.h和Z.h当他们发生变化时A的调用者不必重新编译阻止了级联依赖的发生但是别人仍然能看到私有成员信息
使用Impl的代理模式即A本身只是一个负责对外提供接口的类真正的实现使用一个AImpl类来代理接口的实现通过调用Impl类的对应函数来实现从而实现真正意义上的接口和实现分离
// AImpl.h
struct AImpl
{
public:X getX();Y getY();Z getZ();private:X x;Y y;Z z;
};// A.h
class X;
class Y;
class Z;
struct AImpl;class A
{
public:// 可能的实现 X getX() { return pImpl-getX(); }X getX()Y getY()Z getZ();private:std::tr1::unique_ptrAImpl pImpl;
};任何实现的细节都封装在AImpl类中所以对于调用端来说是完全不可见的包括可能用到的成员。其次只要A的接口没有变化调用端都不需要重新编译。
但是这种实现也有一个问题就是多了一个类需要维护并且每次对A的调用都将是对AImpl的间接调用效率肯定有所降低。
这种实现方式有一些问题需要注意
Impl的声明最好设置为struct原因我也不清楚因为我用class声明的AImpl不包含private成员在Linux上能过在windows过不去一直报LINK ERROR的错误。我怀疑windows上看不到类的定义时直接引用类成员函数会有问题。一般使用unique_ptr来包装Impl类但是使用unique_ptr的时候接口类的析构函数不能直接定义在类的声明中。因为在类的声明中直接定义析构函数或者使用default的时候看不到Impl类的实现也就是看不到Impl类的析构函数而接口类的析构函数必须要看unique_ptr成员函数Impl类的析构函数否则会报can’t delete an incomplete type错误。 这个错误其实是一类错误主要是类的声明不知道类的大小无论是构造还是析构都不知道需要为类的对象分配或者回收的内存大小因此是incomplete type。同时这中前向声明的方式通常也用于解决循环引用的问题但是forward declaration方式被声明的类只能被用于指针因为作为类的成员变量必须知道其大小而声明的Impl类没看到定义不知道大小但是指针的大小是固定的。
Impl
weight.h
#ifndef WEIGHT_H
#define WEIGHT_H
#include memory
class Weight
{
public:Weight();
private:struct Impl;std::unique_ptrImpl m_impl;
};#endif // WEIGHT_Hweight.cpp
#include Weight.h
#include vector
#include stringstruct Weight::Impl {std::string name;std::vectordouble data;
};Weight::Weight(): m_impl(new Impl())
{}将所有需要实例化的成员变量创建一个结构体结构体指针使用unique_ptr管理
但是这种方式在实例化weight的时候会出问题因为unique_ptr内部默认析构器会对指针类型进行判断如果是不完全的类型会进行报错为啥会不完全呢因为编译器默认的析构函数是在头文件隐式内联的在头文件中当然看不到具体类型
解决办法是
让析构的时候看到完整类型呗也就是析构实现的时候看到结构体是完成的所以将weight的析构函数移到.cpp中
#include Weight.h
#include vector
#include stringstruct Weight::Impl {std::string name;std::vectordouble data;
};Weight::Weight(): m_impl(new Impl())
{}
Weight::~Weight() {}也可以使用 ~Weight() default; 相当于实现使用默认的编译器生成代码
#include Weight.h
#include vector
#include stringstruct Weight::Impl {std::string name;std::vectordouble data;
};Weight::Weight(): m_impl(new Impl())
{}Weight::~Weight() default;那么析构有影响拷贝构造和赋值操作符呢
当声明了析构函数编译器就不会默认生成移动操作符函数需要显示声明
那么对于下面的
#ifndef WEIGHT_H
#define WEIGHT_H
#include memory
class Weight
{
public:Weight();~Weight();Weight(Weight rhs) default;Weight operator(Weight rhs) default;
private:struct Impl;std::unique_ptrImpl m_impl;
};#endif // WEIGHT_H因为unique_ptr的原因我们只能使用默认的移动操作符
然而在
#include iostream // std::streambuf, std::cout
#include Weight.h
int main () {Weight w;Weight c;w std::move(c);return 0;
}报错了原因是在 移动操作符的默认实现中 会对原有的进行delete处理这就和析构函数相同了不完整类型
解决办法就是换个地方在.h中统一声明
#ifndef WEIGHT_H
#define WEIGHT_H
#include memory
class Weight
{
public:Weight();~Weight();Weight(Weight rhs);Weight operator(Weight rhs);
private:struct Impl;std::unique_ptrImpl m_impl;
};#endif // WEIGHT_H#include Weight.h
#include vector
#include stringstruct Weight::Impl {std::string name;std::vectordouble data;
};Weight::Weight(): m_impl(new Impl())
{}Weight::~Weight() default;
Weight::Weight(Weight rhs) default;
Weight Weight::operator(Weight rhs) default; 为了保证赋值操作符可以正常使用必须手工自己进行实现
Weight Weight::operator(const Weight rhs) {if (this ! rhs) {*m_impl *rhs.m_impl;}return *this;
} 使用这种赋值方式让结构体内部进行赋值注意的是内存是两块内存只不过现在内容是一样的了 换成shared_ptr后都不需要了
#ifndef WEIGHT_H
#define WEIGHT_H
#include memory
class Weight
{
public:Weight();
private:struct Impl;std::shared_ptrImpl m_impl;
};#endif // WEIGHT_H#include Weight.h
#include vector
#include stringstruct Weight::Impl {std::string name;std::vectordouble data;
};Weight::Weight(): m_impl(new Impl())
{}对于unique_ptr他的析构器是智能指针的一部分因为一开始就可以确定下来这让编译器可以快速执行代码这就要求编译时候看到的指针类型是完全的对于shared_ptr他的内部析构器不是智能指针的一部分属于control Block的一部分所以这也带来的编译器无法优化、减少代码大小
PIMPL的优点
1降低模块的耦合。因为隐藏了类的实现被隐藏的类相当于原类不可见对隐藏的类进行修改不需要重新编译原类。
2降低编译依赖提高编译速度。指针的大小为32位或864位X发生变化指针大小却不会改变文件c.h也不需要重编译。
3接口与实现分离提高接口的稳定性。
1、通过指针封装当定义“new C”或C c1时 ,编译器生成的代码中不会掺杂X的任何信息。
2、当使用C时使用的是C的接口C接口里面操作的类其实是pImpl成员指向的X对象与X无关X被通过指针封装彻底的与实现分离。
参考 编译防火墙