是否有任何理由说下面的类似pimpl的实现不起作用?

时间:2014-01-27 04:17:41

标签: c++ pointers struct cross-platform pimpl-idiom

Here我问了一个问题,试图避免使用window.h文件中的内容污染我的代码库,以确保我的代码库是跨平台兼容的。我被证明,我提出的一般想法提到了pimpl成语。今天我把这个成语向前推了一步或退步了一步,我不确定哪一个。我相当肯定我的实现在技术上不会引起问题,但有点难看。我的主要问题是:是否有人知道实施会导致问题的任何原因?还有什么方法可以减少......嗯......丑陋而且更容易维护?

文件ElementsToHide.h模拟我不想在我的项目中污染全局命名空间的文件内容,例如windows.h。

文件ClassWithPImpl.h包含一个PImpl结构的定义,它只包含一个char [16],但只有在没有定义一个标志来指示它的前一个声明时才定义结构。 char [16]就是为了在所有文件中保持结构的大小相同。

文件ClassWithPImpl.cpp当然定义了类的功能,但与可能包含ClassWithPImpl.h的任何其他文件不同,ClassWithPImpl.cpp定义了它自己的PImpl结构版本,它使用了ElementsToHide.h中的数据类型。这两个结构的大小是相同的,这意味着当ClassWithPImpl.cpp编译时,它能够看到PImpl结构的实际内容。每个其他文件只看到一个私有的char数组。这意味着没有其他文件包含ElementsToHide.h,因此没有其他文件被其数据类型,函数等污染......

main.cpp是一个很小的短程序,它打印出类和PImpl结构的大小,以确保它们是相同的。商业应用程序可能会使用断言来确保结构保持相同的大小。

以下是应用程序中的所有文件: ElementsToHide.h

#ifndef ELEMENTS_TO_HIDE_H
#define ELEMENTS_TO_HIDE_H

typedef short int16;
typedef long int32;
typedef long long int64;

#endif

ClassWithPImpl.h

#ifndef CLASS_WITH_PIMPL_H
#define CLASS_WITH_PIMPL_H

#ifndef PIMPL_DEFINED
#define PIMPL_DEFINED
struct PImpl
{
    char data[16];
};
#else
struct PImpl;
#endif

class ClassWithPImpl
{
private:
    PImpl pImpl;
public:
    ClassWithPImpl();
    void print();
};

#endif

ClassWithPImpl.cpp

#include <iostream>
using namespace std;

#include "ElementsToHide.h"

#define PIMPL_DEFINED
struct PImpl
{
    int16 shortInt;
    int32 mediumInt;
    int64 longInt;
};

#include "ClassWithPImpl.h"

ClassWithPImpl::ClassWithPImpl()
{
    pImpl.shortInt = 4;
    pImpl.mediumInt = 1;
    pImpl.longInt = 2;
}

void ClassWithPImpl::print()
{
    cout << pImpl.shortInt << endl;
    cout << "Size of class as seen from class: " << sizeof(ClassWithPImpl) << endl;
    cout << "Size of PImpl as seen from class: " << sizeof(PImpl) << endl;
}

的main.cpp

#include <iostream>
using namespace std;

#include "ClassWithPImpl.h"

int main()
{
    //int32 shortInt;
    //Program won't compile with this line, because despite the ClassWithPImpl using the int32 type defined in ElementsToHide.h, these types are never
    //actually included into main.cpp.
    ClassWithPImpl().print();
    cout << "Size of Class as seen from Main: " << sizeof(ClassWithPImpl) << endl;
    cout << "Size of PImple as seen from Main: " << sizeof(PImpl) << endl;
    cout << sizeof(short) << ", " << sizeof(long) << ", " << sizeof(long long) << endl;
}

该应用程序的输出是:

4
Size of class as seen from class: 16
Size of PImpl as seen from class: 16
Size of Class as seen from Main: 16
Size of PImple as seen from Main: 16
2, 4, 8

正如您所看到的,类文件可以很好地查看和处理内部变量,而应用程序的其余部分真的无视应用程序具有除char之外的任何内部变量[16]。默认的复制构造函数/运算符应该只用于文件,因为它只是复制数组内容。不需要析构函数,因为类中没有托管资源。还没有浪费的CPU时间引用指向隐藏数据结构的指针,当从实现文件中查看时,它实际上直接包含在类定义中。这很难看。是否有人意识到这种情况不起作用或者会使情况进一步复杂化?有没有什么办法可以简化它而不需要在你想从中获取数据时使用必须被管理和解除引用的指针?

1 个答案:

答案 0 :(得分:0)

指针 -to-implementation idiom使用...等待它......一个指针!因此,本应为私有的数据动态分配new。您发布的代码使用了一个不透明的缓冲区,您 希望 保持足够而不会过度浪费,这会影响pImpl的一些好处但是当它工作时节省了动态分配和释放时间。所以,最好不要将其称为pImpl。

  

我的主要问题是:有人知道实施会导致问题的原因吗?

#ifndef在翻译单元中更改数据隐藏类的内容会给出3.2 / 6的未定义行为:

  

D的每个定义应由相同的令牌序列组成;

所以,不能保证它会起作用。如果您感到幸运,您可能仍然需要检查标准是否保证将您的班级对象放在适当对齐的边界上 - 即使是&#34;内容&#34;表面上是一个char数组,或者是你的实现失败了,或者有一个编译器指令来请求它。

您需要小心构建和销毁放入缓冲区的任何成员数据(我建议您对整个struct使用放置新的和显式的破坏 - 如果方便的话给它构造函数 - 而不是分配给个别成员),并应保持静态断言以确保缓冲区足够大。

  

默认的复制构造函数/运算符应该只用于文件,因为它只是复制数组内容。因为课堂上没有托管资源,所以不需要析构函数。

也许你现在也是,但是你真的认为另一个程序员肯定会在他们想要添加std::string,共享指针或其他东西时仔细检查你做了什么吗?

  

还有什么方法可以减少......嗯......丑陋且更容易维护?

没有什么值得考虑的事情。