使用Structs在C中模拟类

时间:2017-05-21 22:14:15

标签: c class struct

我被限制使用C进行比赛,我需要模拟课程。我正在努力构建一个简单的"点"可以返回并设置点的X和Y坐标的类。然而,下面的代码返回错误,例如"未知类型名称点","期望标识符或("和"期望参数声明符。"这些错误是什么是什么意思?我如何纠正它们?这是编写"伪类的正确方法"?

typedef struct object object, *setCoordinates;

struct object {
    float x, y;
    void (*setCoordinates)(object *self, float x, float y);
    void (*getYCoordinate)(object *self);
    void (*getXCoordinate)(object *self);
};

void object_setCoordinates(object *self, float x, float y){
    self->x = x;
    self->y = y;
}

float object_getXCoordinate(object *self){
    return self->x;
}

float object_getYCoordinate(object *self){
    return self->y;
}

object point;
point.setCoordinates = object_setCoordinates;
point.getYCoordinate = object_getYCoordinate;
point.getXCoordinate = object_getXCoordinate;

point.setCoordinates(&point, 1, 2);
printf("Coordinates: X Coordinate: %f, Y Coordinate: %f", point.getXCoordinate, point.getYCoordinate);

参考: 1. C - function inside struct 2. How do you implement a class in C?

3 个答案:

答案 0 :(得分:3)

您可以按照以下方式更好地实施它:

#include <stdio.h>

struct point {
    float x;
    float y;
};

void point_setCoordinates(struct point *self, float x, float y){
    self->x = x;
    self->y = y;
}

float point_getXCoordinate(struct point *self){
    return self->x;
}

float point_getYCoordinate(struct point *self){
    return self->y;
}

int main(void) {
    struct point my_point;

    point_setCoordinates(&my_point, 1, 2);

    printf("Coordinates: X Coordinate: %f, Y Coordinate: %f\n",
           point_getXCoordinate(&my_point),
           point_getYCoordinate(&my_point));

    return 0;
}

有几点需要注意:

  • 正如@Olaf指出的那样,永远不要键入一个指针 - 它隐藏了你的意图并使事情变得不清楚。是的,它遍及糟糕的API(例如:Windows),但它降低了可读性。
  • 您真的不需要这些功能等同于虚拟功能...只需拥有一组point_*()功能,您可以在point&#39;&#39; 39;
  • 不要用糟糕的名字来混淆......如果它是一个X,Y点,那就称之为 - 不是一个对象(这是一个非常通用的概念)。
  • 您需要在使用printf()的{​​{1}}电话中呼叫功能... - 也就是说您已将其取用,并要求point.getXCoordinate显示该地址好像是printf()
  • 您可能会开始怀疑为什么要关注调用函数来访问透明结构中的变量...请参阅下文。

许多库/ API提供不透明的数据类型。这意味着您可以获得“处理”功能。对某件事情......但你不知道该怎么存储在&#39;中。然后,库为您提供访问功能,如下所示。这就是我建议你处理这种情况的方法。

不要忘记释放记忆!

我已经实施了以下示例。

<强> point.h

float

<强> point.c

#ifndef POINT_H
#define POINT_H

struct point;

struct point *point_alloc(void);
void point_free(struct point *self);

void point_setCoordinates(struct point *self, float x, float y);
float point_getXCoordinate(struct point *self);
float point_getYCoordinate(struct point *self);

#endif /* POINT_H */

<强>的main.c

#include <stdlib.h>
#include <string.h>

#include "point.h"

struct point {
    float x;
    float y;
};

struct point *point_alloc(void) {
    struct point *point;

    point = malloc(sizeof(*point));
    if (point == NULL) {
        return NULL;
    }

    memset(point, 0, sizeof(*point));

    return point;
}

void point_setCoordinates(struct point *self, float x, float y) {
    self->x = x;
    self->y = y;
}

float point_getXCoordinate(struct point *self) {
    return self->x;
}

float point_getYCoordinate(struct point *self) {
    return self->y;
}

void point_free(struct point *self) {
    free(self);
}

答案 1 :(得分:0)

您的代码有一些小错误。这就是为什么它不能编译。

修正了这里:

typedef struct object object;

struct object {
    float x, y;
    void (*setCoordinates)(object *self, float x, float y);
    float (*getYCoordinate)(object *self);
    float (*getXCoordinate)(object *self);
};

void object_setCoordinates(object *self, float x, float y){
    self->x = x;
    self->y = y;
}

float object_getXCoordinate(object *self){
    return self->x;
}

float object_getYCoordinate(object *self){
    return self->y;
}

int main()
{

    object point;
    point.setCoordinates = object_setCoordinates;
    point.getYCoordinate = object_getYCoordinate;
    point.getXCoordinate = object_getXCoordinate;

    point.setCoordinates(&point, 1, 2);
    printf("Coordinates: X Coordinate: %f, Y Coordinate: %f", 
    point.getXCoordinate(&point), point.getYCoordinate(&point));
}

至于方法,当你可以直接调用它们时,可能没有必要将指针存储到struct中的方法:

object x;
object_setCoordinates(x, 1, 2);
//...

答案 2 :(得分:0)

编写需要多态的伪类的另一种方法是创建一个虚拟函数表,并设置构造函数或工厂函数。这是一个假设的例子。 (编辑:现在是MCVE,但对于实际代码,请重构为标题和单独的源文件。)

#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

struct point; // Abstract base class.

struct point_vtable {
  void (*setCoordinates)(struct point *self, float x, float y);
  float (*getYCoordinate)(const struct point *self);
  float (*getXCoordinate)(const struct point *self);
};

typedef struct point {
  const struct point_vtable* vtable;
} point;

typedef struct cartesian_point {
  const struct point_vtable* vtable;
  float x;
  float y;
} cartesian_point;

typedef struct polar_point {
  const struct point_vtable* vtable;
  float r;
  float theta;
} polar_point;

void cartesian_setCoordinates( struct point* self, float x, float y );
float cartesian_getXCoordinate(const struct point* self);
float cartesian_getYCoordinate(const struct point* self);

void polar_setCoordinates( struct point* self, float x, float y );
float polar_getXCoordinate(const struct point* self);
float polar_getYCoordinate(const struct point* self);

const struct point_vtable cartesian_vtable = {
  .setCoordinates = &cartesian_setCoordinates,
  .getXCoordinate = &cartesian_getXCoordinate,
  .getYCoordinate = &cartesian_getYCoordinate
};

const struct point_vtable polar_vtable = {
  .setCoordinates = &polar_setCoordinates,
  .getXCoordinate = &polar_getXCoordinate,
  .getYCoordinate = &polar_getYCoordinate
};

void cartesian_setCoordinates( struct point* const self,
                               const float x,
                               const float y )
{
  assert(self->vtable == &cartesian_vtable);
  struct cartesian_point * const this = (struct cartesian_point*)self;
  this->x = x;
  this->y = y;
}

float cartesian_getXCoordinate(const struct point* const self)
{
  assert(self->vtable == &cartesian_vtable);
  const struct cartesian_point * const this = (struct cartesian_point*)self;
  return this->x;
}

float cartesian_getYCoordinate(const struct point* const self)
{
  assert(self->vtable == &cartesian_vtable);
  const struct cartesian_point * const this = (struct cartesian_point*)self;
  return this->y;
}

void polar_setCoordinates( struct point* const self,
                           const float x,
                           const float y )
{
  assert(self->vtable == &polar_vtable);
  struct polar_point * const this = (struct polar_point*)self;
  this->theta = (float)atan2((double)y, (double)x);
  this->r = (float)sqrt((double)x*x + (double)y*y);
}

float polar_getXCoordinate(const struct point* const self)
{
  assert(self->vtable == &polar_vtable);
  const struct polar_point * const this = (struct polar_point*)self;
  return (float)((double)this->r * cos((double)this->theta));
}

float polar_getYCoordinate(const struct point* const self)
{
  assert(self->vtable == &polar_vtable);
  const struct polar_point * const this = (struct polar_point*)self;
  return (float)((double)this->r * sin((double)this->theta));
}

// Suitable for the right-hand side of initializations, before the semicolon.
#define CARTESIAN_POINT_INITIALIZER { .vtable = &cartesian_vtable,\
                                      .x = 0.0F, .y = 0.0F }
#define POLAR_POINT_INITIALIZER { .vtable = &polar_vtable,\
                                  .r = 0.0F, .theta = 0.0F }

int main(void)
{
  polar_point another_point = POLAR_POINT_INITIALIZER;
  point* const p = (point*)&another_point; // Base class pointer.
  polar_setCoordinates( p, 0.5F, 0.5F ); // Static binding.
  const float x = p->vtable->getXCoordinate(p); // Dynamic binding.
  const float y = p->vtable->getYCoordinate(p); // Dynamic binding.

  printf( "(%f, %f)\n", x, y );
  return EXIT_SUCCESS;  
}

这利用了保证结构的公共初始子序列可以通过指向它们中的任何一个的指针来解决,并且每个实例只存储一个类开销的指针,而不是每个虚函数一个函数指针。您可以使用虚拟表作为变体结构的类标识符。此外,虚拟表不能包含垃圾。虚函数调用需要取消引用两个指针而不是一个指针,但正在使用的任何类的虚拟表很可能都在缓存中。

我还注意到这个界面非常骨架;拥有一个只能转换回笛卡尔坐标的极性类是愚蠢的,这样的任何实现至少需要一些方法来初始化动态内存。

如果您不需要多态性,请参阅Attie更简单的答案。