如何重现C99类方法的行为吗?

问题描述 投票:4回答:3

我有一个简单的代码来计算多边形周边

#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本身的周长。

在单语:我想假的类方法的行为。

c pointers struct
3个回答
4
投票

惯用的解决方案是不暴露函数指针给最终用户,而是使用常规的函数调用,它确实内部指针查找。

float polygon_perimeter(struct polygon *p)
{
    return p->perimeter(p);
}

这是不错的,因为它平行polygon_init()polygon_destroy():所有的功能都是“阶级”,而不是通过成员指针只是因为它是多态被称为这个特殊的外面。

它也避免了泄漏struct polygon的内部用户。如果你需要的所有访问“类”被通过不透明指针的函数调用库做有很多好处。没有成员直接访问,所以struct的布局可以在不危及API破损随意更改。

如果你走这条路,那么你可以通过只提供在polygon.h一个空着的声明完全掩盖其内部:

// polygon.h
struct polygon;

4
投票

我想知道,如果有一种方法叫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),你可以调用对应于参数的类型的功能。


0
投票

在polygon_init初始化:

poly->perimeter = perimeter;

如果你的对象接口得到广大虽然,你可能希望实现适当的虚拟表(VT),这仅仅是一个一类函数指针的结构。然后,每个对象存储的指针VT,而不是所有的功能独立,而且永远只需要一个单一的常量VT对于任何给定的类。这样一个对象的大小不为多少虚拟方法有一个功能改变。

© www.soinside.com 2019 - 2024. All rights reserved.