包含标准库和本地头文件的顺序



我正在生成位图文件。该程序每次都编译,但取决于#include在某些点的顺序,它给我好的或损坏的。bmp文件。
这是一个教程,所有的文件都在这里。

结构如下:

main.cpp

#include <iostream>
#include "Bitmap.h"
using namespace std;
int main() {
Bitmap bitmap(800, 600);
bitmap.write("test.bmp");
}

Bitmap.h

#include <string>
#include <cstdint>
#include <memory>
using namespace std;
class Bitmap {
private:
int m_width{0};
int m_height{0};
unique_ptr<uint8_t[]> m_pPixels{nullptr};
public:
Bitmap(int width, int height);
bool write(string filename);
};

BitmapFileHeader.h

#include <cstdint>
using namespace std;
#pragma pack(2)
struct BitmapFileHeader {
char header[2] { 'B', 'M' };
int32_t fileSize;
int32_t reserved { 0 };
int32_t dataOffset;
};

BitmapInfoHeader.h

#include <cstdint>
using namespace std;
#pragma pack(2)
struct BitmapInfoHeader {
int32_t headerSize{40};
int32_t width;
int32_t height;
int16_t planes{1};
int16_t bitsPerPixel{24};
int32_t compression{0};
int32_t dataSize{0};
int32_t horizontalResolution{2400};
int32_t verticalResolution{2400};
int32_t colors{0};
int32_t importantColors{0};
};

Bitmap.cpp

#include <fstream>
#include "Bitmap.h"
#include "BitmapInfoHeader.h"
#include "BitmapFileHeader.h"
using namespace std;
Bitmap::Bitmap(int width, int height): m_width(width), m_height(height), m_pPixels(new uint8_t[width * height * 3]{ }) {}
bool Bitmap::write(string filename) {
BitmapFileHeader fileHeader;
BitmapInfoHeader infoHeader;
fileHeader.fileSize = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + m_width * m_height * 3;
fileHeader.dataOffset = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader);
infoHeader.width = m_width;
infoHeader.height = m_height;
ofstream file;
file.open(filename, ios::out | ios::binary);
file.write((char *)&fileHeader, sizeof(fileHeader));
file.write((char *)&infoHeader, sizeof(infoHeader));
file.write((char *)m_pPixels.get(), m_width*m_height*3);
file.close();
return true;
}

现在,当在Bitmap.cpp中,它是在这个顺序,一切都很好。当我改变顺序,使#include <fstream>是最后一个,它仍然编译没有错误,但产生损坏的文件。
BitmapInfoHeader.hBitmapInfoHeader.h只是持有一个结构体,其中有一些int32_t变量,根本不使用fstream
为什么呢?

错误在BitmapInfoHeader.h

// issue #1:  There is no guard to prevent re-entry into you header.
#pragma once   // this should fix that, you can also place a classic 
// #ifndef / #define / #endif guard, as advised by the 
// C++ Core Guidelines  
// https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines 
#include <cstdint>
using namespace std; // Issue #2: 
// NEVER place a "using namespace std;" in a header 
// file.  BTW, it is not even needed for this
// file to compile.
// And NEVER place a "using namespace ...;"
// in the global namespace in a header file. 
// issue # 3.  Any pack pragma MUST be preceded by a push, like this:
#pragma pack(2)                // <-- This pack pragma breaks all include
// files that will follow this one.
#pragma pack(push)
#pragma pack(1)                // use pack(1) to signify full packing,
// so when someone else reads your code, 
// they won't need to read the whole 
// struct definition, to figure out 
// what it is you are trying to
// accomplish. 
struct BitmapInfoHeader {
int32_t headerSize{40};
int32_t width;
int32_t height;
int16_t planes{1};
int16_t bitsPerPixel{24};
int32_t compression{0};
int32_t dataSize{0};
int32_t horizontalResolution{2400};
int32_t verticalResolution{2400};
int32_t colors{0};
int32_t importantColors{0};
};
//  After a #pragma(push), don't forget to restore the original 
//  packing, otherwise, you are violating the One Definition Rule.
//  If you do not restore, everything after including this file
//  will exhibit undefined behavior.  
#pragma pack(pop)

文件Bitmap.h也有一些相同的问题。

没有mcve是不可能确定的。但是,当包含顺序中断编译时,通常是由于至少一个头文件未能包含其所有依赖项。在这种情况下,如果另一个包含缺失依赖的头文件恰好包含在被破坏的头文件之前,它会导致错误被隐藏。

编辑添加的代码:

如果将标准头移到#pragma pack(2)下面,就破坏了标准类定义。你应该总是在头文件中push和pop packing pragma。

最新更新