header 问题中的 C++ 实现
C++ implementation in header issues
我正在处理一个相当大的项目(3D 图形引擎),我 运行 在重构代码时遇到了一些麻烦。我想在单个文件中实现所有 classes(只有 .hpp 而不是每个 class 都有 .cpp 和 .hpp 文件)。我没有特别的理由这样做,只是想这样做,但我希望避免讨论什么是 C++ 最佳实践。
当我这样做时,我得到了一系列如下所示的多重定义错误:
/tmp/ccztDQam.o: In function `Point3DH::normalize()':
Renderer.cpp:(.text+0x736): multiple definition of `Point3DH::normalize()'
/tmp/ccawpiuU.o:main.cpp:(.text+0x1a6de): first defined here
/tmp/ccztDQam.o: In function `Point3DH::dot(Point3DH, Point3DH)':
Renderer.cpp:(.text+0x79e): multiple definition of `Point3DH::dot(Point3DH, Point3DH)'
/tmp/ccawpiuU.o:main.cpp:(.text+0x1a746): first defined here
/tmp/ccztDQam.o: In function `Point3DH::cross(Point3DH, Point3DH)':
Renderer.cpp:(.text+0x7d6): multiple definition of `Point3DH::cross(Point3DH, Point3DH)'
/tmp/ccawpiuU.o:main.cpp:(.text+0x1a77e): first defined here
...
当 classes 开始相互包含并且代码重复多次时,问题就出现了。正如 this 答案中所解释的那样,header 守卫似乎还不够。我想知道是否有任何方法可以解决这个问题或实现目标的替代方法。
该项目被组织成模块(文件夹),例如包含相关 class 的几何或多边形,因此包含路径转到 parent 目录,然后进入正确的模块和 class
供参考,这是其中一个文件的样子 (./graphics/Raster.hpp):
#ifndef GRAPHICS_RASTER
#define GRAPHICS_RASTER
#include "../graphics/Colour.hpp"
#include <vector>
class Raster {
private:
std::vector<Colour> image;
std::vector<double> zBuffer;
int width;
int height;
public:
Raster(int, int, Colour);
void setPixel(int, int, double, Colour);
int getWidth();
int getHeight();
};
#endif
#ifndef GRAPHICS_RASTER_IMPLEMENTATION
#define GRAPHICS_RASTER_IMPLEMENTATION
#include "../graphics/Colour.hpp"
#include <vector>
#include <limits>
Raster::Raster(int width, int height, Colour clear) :
image(std::vector<Colour>(width*height, clear)),
zBuffer(std::vector<double>(width*height, -std::numeric_limits<double>::max())),
width(width),
height(height)
{}
void Raster::setPixel(int x, int y, double z, Colour c) {
if(x < 0 || x >= width || y < 0 || y >= height) return;
if(z <= zBuffer[(height - y - 1)*width + x]) return;
image[(height - y - 1)*width + x] = c;
zBuffer[(height - y - 1)*width + x] = z;
}
int Raster::getWidth() {return width;}
int Raster::getHeight() {return height;}
#endif
每次您的 header 包含在 cpp 文件中时,您都会创建一个新的实现副本。
您需要确保该实现仅在一个 cpp 文件中使用 - 或者内联每个方法。
如果您出于某种原因想要在 header 文件中实现所有内容,则必须将所有函数内联。 class 定义中定义的函数是隐式内联的。定义 out-of-class 的函数必须使用 inline
关键字显式声明。
这就是您必须对 header 的 "implementation" 部分中的每个定义执行的操作 - 向每个函数定义添加显式 inline
关键字。例如
inline void Raster::setPixel(int x, int y, double z, Colour c) {
if(x < 0 || x >= width || y < 0 || y >= height) return;
if(z <= zBuffer[(height - y - 1)*width + x]) return;
image[(height - y - 1)*width + x] = c;
zBuffer[(height - y - 1)*width + x] = z;
}
等等。
当然,您也可以将所有成员函数定义移动到 class 定义中(这将使它们内联),但这将排除这种明显分离的 two-section header 结构就像你现在一样。不知道对你有多重要
本指南对此有很好的想法:
https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
示例:
https://github.com/nothings/stb
基本上:
1-制作一个#define UNIQUE_NAME_IMP和#define UNIQUE_NAME_HEADER使实现和声明可见在不同的文件上使用:
您的实施:
#ifdef _DECL_
type declaration
function prototype
#endif
#ifdef _IMPL_
code
#endif
并在将使用它的另一个文件中:
#define _DECL_
#include <my_header.h>
code...
...
//use this only once to avoid
//duplicate symbol like you mentioned in your post.
#define _IMPL_
#include <my_header.h>
2-避免内存分配,让您的函数使用您通过结构传递的内存。
3-避免外部依赖。在使用 header...
之前,每个依赖项都会让您使用标志或创建要求来遵守
4-使用 "static"。这使得实现对于创建它的源文件是私有的。
我正在处理一个相当大的项目(3D 图形引擎),我 运行 在重构代码时遇到了一些麻烦。我想在单个文件中实现所有 classes(只有 .hpp 而不是每个 class 都有 .cpp 和 .hpp 文件)。我没有特别的理由这样做,只是想这样做,但我希望避免讨论什么是 C++ 最佳实践。
当我这样做时,我得到了一系列如下所示的多重定义错误:
/tmp/ccztDQam.o: In function `Point3DH::normalize()':
Renderer.cpp:(.text+0x736): multiple definition of `Point3DH::normalize()'
/tmp/ccawpiuU.o:main.cpp:(.text+0x1a6de): first defined here
/tmp/ccztDQam.o: In function `Point3DH::dot(Point3DH, Point3DH)':
Renderer.cpp:(.text+0x79e): multiple definition of `Point3DH::dot(Point3DH, Point3DH)'
/tmp/ccawpiuU.o:main.cpp:(.text+0x1a746): first defined here
/tmp/ccztDQam.o: In function `Point3DH::cross(Point3DH, Point3DH)':
Renderer.cpp:(.text+0x7d6): multiple definition of `Point3DH::cross(Point3DH, Point3DH)'
/tmp/ccawpiuU.o:main.cpp:(.text+0x1a77e): first defined here
...
当 classes 开始相互包含并且代码重复多次时,问题就出现了。正如 this 答案中所解释的那样,header 守卫似乎还不够。我想知道是否有任何方法可以解决这个问题或实现目标的替代方法。
该项目被组织成模块(文件夹),例如包含相关 class 的几何或多边形,因此包含路径转到 parent 目录,然后进入正确的模块和 class
供参考,这是其中一个文件的样子 (./graphics/Raster.hpp):
#ifndef GRAPHICS_RASTER
#define GRAPHICS_RASTER
#include "../graphics/Colour.hpp"
#include <vector>
class Raster {
private:
std::vector<Colour> image;
std::vector<double> zBuffer;
int width;
int height;
public:
Raster(int, int, Colour);
void setPixel(int, int, double, Colour);
int getWidth();
int getHeight();
};
#endif
#ifndef GRAPHICS_RASTER_IMPLEMENTATION
#define GRAPHICS_RASTER_IMPLEMENTATION
#include "../graphics/Colour.hpp"
#include <vector>
#include <limits>
Raster::Raster(int width, int height, Colour clear) :
image(std::vector<Colour>(width*height, clear)),
zBuffer(std::vector<double>(width*height, -std::numeric_limits<double>::max())),
width(width),
height(height)
{}
void Raster::setPixel(int x, int y, double z, Colour c) {
if(x < 0 || x >= width || y < 0 || y >= height) return;
if(z <= zBuffer[(height - y - 1)*width + x]) return;
image[(height - y - 1)*width + x] = c;
zBuffer[(height - y - 1)*width + x] = z;
}
int Raster::getWidth() {return width;}
int Raster::getHeight() {return height;}
#endif
每次您的 header 包含在 cpp 文件中时,您都会创建一个新的实现副本。
您需要确保该实现仅在一个 cpp 文件中使用 - 或者内联每个方法。
如果您出于某种原因想要在 header 文件中实现所有内容,则必须将所有函数内联。 class 定义中定义的函数是隐式内联的。定义 out-of-class 的函数必须使用 inline
关键字显式声明。
这就是您必须对 header 的 "implementation" 部分中的每个定义执行的操作 - 向每个函数定义添加显式 inline
关键字。例如
inline void Raster::setPixel(int x, int y, double z, Colour c) {
if(x < 0 || x >= width || y < 0 || y >= height) return;
if(z <= zBuffer[(height - y - 1)*width + x]) return;
image[(height - y - 1)*width + x] = c;
zBuffer[(height - y - 1)*width + x] = z;
}
等等。
当然,您也可以将所有成员函数定义移动到 class 定义中(这将使它们内联),但这将排除这种明显分离的 two-section header 结构就像你现在一样。不知道对你有多重要
本指南对此有很好的想法:
https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
示例:
https://github.com/nothings/stb
基本上:
1-制作一个#define UNIQUE_NAME_IMP和#define UNIQUE_NAME_HEADER使实现和声明可见在不同的文件上使用:
您的实施:
#ifdef _DECL_
type declaration
function prototype
#endif
#ifdef _IMPL_
code
#endif
并在将使用它的另一个文件中:
#define _DECL_
#include <my_header.h>
code...
...
//use this only once to avoid
//duplicate symbol like you mentioned in your post.
#define _IMPL_
#include <my_header.h>
2-避免内存分配,让您的函数使用您通过结构传递的内存。
3-避免外部依赖。在使用 header...
之前,每个依赖项都会让您使用标志或创建要求来遵守4-使用 "static"。这使得实现对于创建它的源文件是私有的。