标题问题中的C ++实现

时间:2018-01-05 06:23:25

标签: c++ compilation header-files

我正在开发一个相当大的项目(3D图形引擎),并且在重构代码时遇到了一些麻烦。我希望我的所有类都在单个文件中实现(只有.hpp,而不是每个类都有.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
...

当类开始包含彼此并且代码重复多次时,问题就出现了。似乎this答案中解释的标题保护不够。我想知道是否有办法绕过这个或另一种方法来实现目标。

项目被组织成几何或多边形等模块(文件夹),其中包含相关的类,因此包含路径转到父目录,然后转到正确的模块和类

供参考,以下是其中一个文件(./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

3 个答案:

答案 0 :(得分:4)

如果由于某种原因想要在头文件中实现所有内容,则必须将所有函数内联。类定义中定义的函数是隐式内联的。必须使用inline关键字明确声明类外定义的函数。

这就是您在标题的“实现”部分中使用的每个定义所要做的 - 为每个函数定义添加显式inline关键字。 E.g。

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; 
}

等等。

当然,您也可以将所有成员函数定义移动到类定义中(这将使它们成为内联),但这将排除您现在拥有的这种明显分离的两节标题结构。我不知道这对你有多重要。

答案 1 :(得分:2)

每次将标头包含在cpp文件中时,都会创建一个新的实现副本。

您需要确保实现仅在一个cpp文件中使用 - 或者内联每个方法。

答案 2 :(得分:1)

本指南对此有很好的想法:

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 - 避免外部依赖。在使用标题之前,每个依赖项都会使您使用标记或创建要求...

4次使用“静态”。这使得实现对创建它的源文件是私有的。