C++ 使用预处理器声明多个类似的类是一种良好的做法吗?
假设我想创建一个数学库。我需要在不同维度中操作向量,因此我希望每个维度有一个类(也称为C++ 使用预处理器声明多个类似的类是一种良好的做法吗?,c++,c-preprocessor,C++,C Preprocessor,假设我想创建一个数学库。我需要在不同维度中操作向量,因此我希望每个维度有一个类(也称为Vector2,Vector3,Vector4) 到目前为止还不错。但这将导致严重的代码重复,因为Vector3主要是一个Vector2,在某些函数中使用了z属性 所以我有个主意。代码复制是机器的任务,而不是人类的任务,因此我可以写以下内容: 在Vector.hpp中: #ifndef VECTOR_HPP #define VECTOR_HPP #define VECTOR_DIM 2 #include "_
Vector2
,Vector3
,Vector4
)
到目前为止还不错。但这将导致严重的代码重复,因为Vector3
主要是一个Vector2
,在某些函数中使用了z
属性
所以我有个主意。代码复制是机器的任务,而不是人类的任务,因此我可以写以下内容:
在Vector.hpp中:
#ifndef VECTOR_HPP
#define VECTOR_HPP
#define VECTOR_DIM 2
#include "_Vector.hpp"
#define VECTOR_DIM 3
#include "_Vector.hpp"
#define VECTOR_DIM 4
#include "_Vector.hpp"
#undef VECTOR_DIM
#endif
// This header was not protected from multiple inclusions on purpose
#define VECTOR_NAME Vector ## VECTOR_DIM
class VECTOR_NAME
{
public:
// Some methods here ...
float x;
float y;
#if VECTOR_DIM >= 3
float z;
#endif
#if VECTOR_DIM >= 4
float w;
#endif
};
#undef VECTOR_NAME
在_Vector.hpp中:
#ifndef VECTOR_HPP
#define VECTOR_HPP
#define VECTOR_DIM 2
#include "_Vector.hpp"
#define VECTOR_DIM 3
#include "_Vector.hpp"
#define VECTOR_DIM 4
#include "_Vector.hpp"
#undef VECTOR_DIM
#endif
// This header was not protected from multiple inclusions on purpose
#define VECTOR_NAME Vector ## VECTOR_DIM
class VECTOR_NAME
{
public:
// Some methods here ...
float x;
float y;
#if VECTOR_DIM >= 3
float z;
#endif
#if VECTOR_DIM >= 4
float w;
#endif
};
#undef VECTOR_NAME
这将大大简化任务,但这是一种好的做法吗?鉴于您发布的代码,您可以轻松地用以下代码替换它,而不必使用预处理器技巧
template <int dim> struct Data;
template <> struct Data<2>
{
float x;
float y;
};
template <> struct Data<3> : Data<2>
{
float z;
};
template <> struct Data<4> : Data<3>
{
float w;
};
template <int dim> class Vector
{
public:
// Some methods here ...
Data<dim> data;
};
模板结构数据;
模板结构数据
{
浮动x;
浮动y;
};
模板结构数据:数据
{
浮动z;
};
模板结构数据:数据
{
浮动w;
};
模板类向量
{
公众:
//这里有一些方法。。。
数据;
};
您可以轻松扩展向量
和数据
,以支持更多功能
不仅如此,您还可以在实例化Vector
时防止dim
的无效值,方法是确保Data
仅为dim
的预设有效值集定义dim功能上的相似性会导致代码中的重复?
考虑到帖子标题中的问题,“使用预处理器良好实践声明多个类似类吗?”首先,让我们看看是否应该消除重复。是的,您读对了-,这取决于副本的上下文
作为经验法则
- 巧合的复制应保留
- 应排除系统性的重复
请注意,“巧合”并不意味着“意外”。相反,偶然重复的代码通常会有意识地编写。关键的一点是,类似的功能没有一个基本的共同原则。因此,即使它们今天碰巧是相似的,它们能够独立进化也是很重要的
还要注意,“系统化”并不一定意味着你已经计划好了。这与“系统错误”中的“系统性”相同,即重复是由于潜在的共同原则造成的,无论是否有意识
载体
应用于你在问题描述中给出的例子,数学库中不同维度的向量类型,可以说很多重复都是系统类型的,应该消除。但在其他情况下,它可能不会如此清晰
如何消除不必要的重复?
我们可以使用的工具
如果它发生在编译时,您在C中的选项将受到限制。但是,你把这个问题标记为<强> C++ >强>一个,即使C和C++共享他们的预编译器,C++也有更多的功能:内联函数、实用型和最后但不是最少的模板类和模板函数。
已经很好地展示了如何将模板应用到您的案例中。更多的是可能的模板:在C++中,有一个完整的开发学科,叫做“模板元编程”。
与快速使用预处理器相比,使用模板解决方案要困难一些。那么,为什么要选择模板而不是预处理器宏和包含
想想你的(图书馆)用户
对于只供自己使用且不需要大量维护的代码来说,获得“有效”的东西可能就足够了。但是如果您正在编写一个库,那么当用户在使用该库时出现可检测到的错误时,他们会希望得到编译器的通知。作为推论,您需要知道,当以无错误编译的方式调用库时,它会执行一些有意义的操作。对于非平凡逻辑,要正确使用宏是非常困难的,因为您必须考虑以下几个问题:
- 保证类型安全
- 如果宏带有参数,那么这些参数还会是宏吗
- 如果宏采用参数,那么这些参数可以是函数调用还是方法调用?
- 如果这些功能有副作用呢?(您必须小心,即使宏可能多次使用函数的返回值,也不会多次调用函数。)
- 宏没有名称空间,因此必须通过名称前缀约定来避免冲突
虽然使用模板一开始可能很难获得有效的东西,但使用makros获得正确有效的东西要比使用makros容易得多。出于其他答案中提到的原因,我建议不要这样做(使用模板可能是更好的选择)。也就是说,在某些情况下,预处理器和宏可能会派上用场。我已经成功地使用了宏,在这种情况下,我知道类似于代码的类很小,不太可能更改(声明和实现)或被继承,并且使用模板实现会很难编写/理解,因此更容易出现错误。事实上,这段代码还没有被再次触动,工作顺利,我仍然认为宏是最好的选择
不过,根据问题中提供的信息,我觉得你没有这种情况。但是,使用任何对你有用的东西,特别是当它是一个个人项目时:如果它在将来咬到你,你就会学到一些东西为什么不使用C++模板?预处理器宏几乎总是一个坏主意。你不能用这种方式实现一个有意义的接口。也可以用d代替