C中结构体多态的最佳方法是什么?

14

我正在编写一个简单的二维向量对象。它将拥有x和y分量以及长度、叉积等方法。问题在于,我希望该结构具有许多可能类型(char、int、float、double等)。我想知道从设计上来看,与对象交互的最佳选择是什么?以下是我目前正在考虑的:

1. 让用户将向量对象传递给专门的函数,例如:

Vector2Dt_Dot(Vec2Dt* vector1, Vec2Dt* vector2);

其中,'t'是向量的类型。然而,这种方法的问题在于它不允许不同类型相互交互,因此我不能计算浮点向量2d和双精度向量2d的点积。第二种方法,也是我倾向的方法:

2. 让用户将向量对象作为void指针传递,同时传递它们的类型,例如:

Vector2D_Dot(void* vector1, unsigned vector1_type, void* vector2, unsigned vector2_type);

显然,这种方法在API方面更加紧凑,也解决了上述问题,但代价是增加了一些额外的参数和类型安全。可能还有其他我不知道的解决方案,但目前我正在考虑这些。你认为哪种方法最好?

@WhozCraig 这个问题标记为 c,而不是 c++ - Ismail Badawi
不要使用void *的替代方案;因为在C或C++中,任何指针类型都可以转换为void *,所以你可能会犯很多编译器无法捕捉到的错误。曾经有一段时间,这个问题(简短地)带有C++标签,并且更长时间使用了C++引用类型符号。如果C++相关,请考虑使用vector<vector<yourtype>> - Jonathan Leffler
@johnathan leffler:是的,那是我的错误。我经常在C和C++之间切换。如果在C中有类似于向量的接口选项,我肯定会使用它。 - Shokwav
3个回答

22

你可以使用多态对象。将结构定义为:

#define INT_TYPE 0
#define DOUBLE_TYPE 1
//more type constants

typedef struct Vector2D {
    int type;
} Vector2D;

typedef struct Vector2D_int {
    Vector2D super;
    int x, y;
} Vector2D_int;

typedef struct Vector2D_double {
    Vector2D super;
    double x, y;
} Vector2D_double;

//more typed vector structures

然后你可以编写函数来接受Vector2D指针,检查它们各自的类型字段并将它们向下转换为适当类型的变量以访问有效负载数据。

double Vector2D_length(const Vector2D* vector) {
    if(vector->type == TYPE_INT) {
        const Vector2D_int* intVector = (Vector2D_int*)vector;
        return sqrt(intVector->x * intVector->x + intVector->y * intVector->y);
    }
    if(vector->type == TYPE_DOUBLE) {
        const Vector2D_double* doubleVector = (Vector2D_double*)vector;
        return sqrt(doubleVector->x * doubleVector->x + doubleVector->y * doubleVector->y);
    }
    //other cases follow
}

这是手写的多态性。你需要确保的是,type字段始终设置为正确的值(在创建类型化向量时设置一次)。

相对于你的第二个想法,这种方法的优点在于,你不必再另外传递向量的类型,这会使使用向量变得棘手且易出错。

作为替代方案,可以将type字段定义为指向函数指针结构体的指针。您可以为每个定义的带类型向量类型创建一个此函数指针结构体对象,并使用它来查找要在给定向量中使用的函数。 这种方法非常接近C ++在内部的操作方式。


2

您可以使用可变参数列表,它的原型被编码为:

int  xyz(int a, ...);

这种风格需要一个明确定义的参数,在本例中为a,后面可以跟任意数量的数据类型可以在运行时确定的参数。

有关如何处理可变参数列表的完整描述,请参见函数和对象:va_listva_startva_args;和va_end

希望这可以帮助您。如果对va_list等有疑问,请提出。


0
我实际采用的是以下内容:
我创建了一个基础的 Vector2D 类,具有以下布局:
struct Vector2D_Base;
typedef struct Vector2D_Base{
    M_double (*Vector2D_Get_X)(struct Vector2D_Base* vec);
    M_double (*Vector2D_Get_Y)(struct Vector2D_Base* vec);
} Vector2D;

正如您所看到的,这使得通用向量函数能够调用它们以获取派生类的x和y值转换为double,从而防止通用函数担心char和float等类型大小之间的差异。然后每个派生类:

#define DEFINE_VECTOR2D(type, name)\
typedef struct{\
Vector2D_Base vec_base;\
type x, y;\
} Vector2D##name\

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接