我有一个简单的代码来计算多边形周边
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
struct point { float x; float y; };
struct polygon
{
int sides;
struct point vertex[20]; // No more than 20 sided polygon
float (*perimeter)(struct polygon * p);
};
struct polygon * polygon_init(int sides)
{
struct polygon *poly;
poly = malloc(sizeof *poly);
poly->sides = sides;
return poly;
}
void polygon_destroy(struct polygon * poly)
{
free(poly);
}
float distance(struct point p1, struct point p2)
{
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
// Assuming points are ordered ;-)
float perimeter(struct polygon * poly)
{
float p = 0;
for (int i = 0; i < poly->sides - 1; i++)
p += distance(poly->vertex[i], poly->vertex[i+1]);
return p + distance(poly->vertex[poly->sides - 1], poly->vertex[0]);
}
int main(void)
{
struct polygon *p1 = polygon_init(3);
struct point t[3] = { {0, 0}, {.5, 1}, {1, 0}};
memcpy(p1->vertex, t, sizeof(t));
p1->perimeter = &perimeter;
printf("perimeter = %.2f\n", p1->perimeter(p1));
polygon_destroy(p1);
return 0;
}
正如你可以看到,我用一个函数指针分配“自定义”功能来计算周长(p1->perimeter = &perimeter;
)。随着我可以根据分配给p1
的结构“抽象”的号召。
但随着明显,我传递给自身结构作为参数传递给perimeter()
功能p1->perimeter(p1)
参考,这看起来非常多余。
我想知道,如果有一种方法叫p1->perimeter()
,而不是拨打以上和内perimeter()
功能会自动地(知道我引用p1
),表示将计算p1
本身的周长。
在单语:我想假的类方法的行为。
惯用的解决方案是不暴露函数指针给最终用户,而是使用常规的函数调用,它确实内部指针查找。
float polygon_perimeter(struct polygon *p)
{
return p->perimeter(p);
}
这是不错的,因为它平行polygon_init()
和polygon_destroy()
:所有的功能都是“阶级”,而不是通过成员指针只是因为它是多态被称为这个特殊的外面。
它也避免了泄漏struct polygon
的内部用户。如果你需要的所有访问“类”被通过不透明指针的函数调用库做有很多好处。没有成员直接访问,所以struct
的布局可以在不危及API破损随意更改。
如果你走这条路,那么你可以通过只提供在polygon.h
一个空着的声明完全掩盖其内部:
// polygon.h
struct polygon;
我想知道,如果有一种方法叫
p1->perimeter()
,而不是拨打以上和内perimeter()
功能会自动地(知道我引用p1
),表示将计算p1
本身的周长。
C不提供任何满意的方式传达给perimeter()
到对其他不是通过函数的参数进行操作的对象。有各种各样的,通过它可以存储指向某处的函数知道如何寻找它的struct polygon
机制,但这些都是杂乱和/或不是线程安全的,所有的人都需要某种操作或额外的代码除了功能选择和功能调用。
该->
运营商根本就不是其(左)的操作数的任何信息传达到它的结果,即使它确实,C函数调用表达式只能通过函数的参数信息传达给被调用的函数。
@ JohnKugelman的答案已经给出了地道的Ç处理这一类问题:通过对象在作为参数进行操作。其中一般不说,而不是通过操作对象的成员选择功能,并以这种方式避免你的提问时指出冗余。
要在相反的方向,那么,有一些好玩的游戏,你可以在C11使用generic selection选择功能发挥和以后调用基于参数的类型。一个典型的包装,在一个通用的宏。也许这将减少拍摄功能上,他们都应该操作的对象的成员的重要性给您。例如:
#define perimeter(x) _Generic((x), \
struct circle *: circle_perimeter, \
struct polygon *: poly_perimeter, \
struct path *: path_perimeter)(x)
然后,当你调用perimeter(a_shape_pointer)
,你可以调用对应于参数的类型的功能。
在polygon_init初始化:
poly->perimeter = perimeter;
如果你的对象接口得到广大虽然,你可能希望实现适当的虚拟表(VT),这仅仅是一个一类函数指针的结构。然后,每个对象存储的指针VT,而不是所有的功能独立,而且永远只需要一个单一的常量VT对于任何给定的类。这样一个对象的大小不为多少虚拟方法有一个功能改变。