Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/146.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 是否有任何理由认为以下类似pimpl的实现不会';不行?_C++_Pointers_Struct_Cross Platform_Pimpl Idiom - Fatal编程技术网

C++ 是否有任何理由认为以下类似pimpl的实现不会';不行?

C++ 是否有任何理由认为以下类似pimpl的实现不会';不行?,c++,pointers,struct,cross-platform,pimpl-idiom,C++,Pointers,Struct,Cross Platform,Pimpl Idiom,我问了一个问题,试图避免Windows.h文件中的内容污染我的代码库,以确保我的代码库是跨平台兼容的。有人告诉我,我提出的一般想法是指pimpl习语。今天我把这个成语理解为前进一步还是后退一步,我不确定是哪一步。我相当肯定我的实现在技术上不会造成问题,但有点难看。我的主要问题是:有人知道实施会带来问题的原因吗?还有什么方法可以让它变得不那么。。。好丑陋和更容易维护 文件ElementsToHide.h模拟我不希望在项目中污染全局命名空间的文件内容,例如windows.h 文件ClassWithP

我问了一个问题,试图避免Windows.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结构的实际内容。其他每个文件只看到一个私有的字符数组。这意味着没有任何其他文件包含ElementsToHide.h,因此没有任何其他文件被其数据类型、函数等污染

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

以下是应用程序中的所有文件: 元素隐藏

#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;
}
正如您所看到的,类文件可以很好地查看和处理内部变量,而应用程序的其余部分实际上没有注意到应用程序有除char之外的任何内部变量[16]。默认的复制构造函数/运算符应该只在文件中工作,因为它只在数组内容上进行复制。不需要析构函数,因为类中没有托管资源。引用指向隐藏数据结构的指针也不会浪费CPU时间,从实现文件中查看时,它实际上直接包含在类定义中。它很难看。有人知道这样做行不通或会使事情更加复杂的情况吗?有没有什么方法可以简化它,而不必求助于指针,只要您想从它获取数据,指针就必须被管理和取消引用?

指针到实现习惯用法使用。。。等等。。。一个指针!因此,将使用
new
动态分配本来是私有的数据。您发布的代码使用了一个不透明的缓冲区,希望在不过度浪费的情况下仍能保持足够的缓冲区,这损害了pImpl的一些好处,但在它起作用时节省了动态分配和释放时间。因此,最好不要将其称为pImpl

我的主要问题是:有人知道实施会带来问题的原因吗

根据3.2/6的规定,
#ifndef
在翻译单元之间更改数据隐藏类的内容会产生未定义的行为:

D的每个定义应包含相同的令牌序列

所以,不能保证它会起作用。如果幸运的话,您可能仍然希望检查标准是否保证将类对象放在适当对齐的边界上——即使“内容”表面上是一个字符数组,或者您的实现没有做到这一点,或者有编译器指令请求它

您需要小心地构造和销毁放入缓冲区中的任何成员数据(我建议您对整个
结构使用新的和显式的销毁方法,如果方便的话,为其提供构造函数,而不是分配给单个成员),并且应该维护静态断言,以确保缓冲区足够大

默认的复制构造函数/运算符应该只在文件中工作,因为它只在数组内容上进行复制。不需要析构函数,因为类中没有托管资源

也许现在对你来说是这样,但是你真的认为当其他程序员只想添加
std::string
、共享指针或其他东西时,他们一定会仔细检查你所做的事情吗

还有什么方法可以让它变得不那么。。。好丑陋和更容易维护


没有什么值得你去想的。

啊哈!我假设Pimpl是私有实现的缩写。如果它代表指向实现的指针,那么从技术上讲,它显然不是PImpl实现。更新主题标题以使其更适合。其余的问题是为什么我希望找到一些更好的方法来实现这一点,我对自己理解为什么实现有效的能力充满信心。。。但我不相信每一个程序员,甚至大多数程序员都能理解正在做什么、怎么做、为什么做。“达林斯:我认为85%的专业C++程序员会理解在结构旁边给出三行或四行注释。我已经多次看到这种“改造”,所以很多人都理解其动机。不过,我还是放弃了
#ifndef
,只使用了一个简单的私有成员函数,该函数重新解释并强制转换字符数组以返回对实际数据结构的引用。我甚至没有想过使用重新解释
#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