使用结构模拟C中的类

yrdbyhpb  于 11个月前  发布在  其他
关注(0)|答案(5)|浏览(97)

我不得不使用C进行竞争,我需要模拟类。我试图构造一个简单的“point”类,它可以返回并设置点的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?

1yjd4xko

1yjd4xko1#

你会做得更好,以实现它如下:

#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所指出的,永远不要为指针键入def--它隐藏了你的意图,让事情变得不清楚。是的,这都是因为糟糕的API(例如:Windows),但它降低了可读性。
  • 你真的不需要这些函数等同于虚函数.只需在point 'thing'上调用一组point_*()函数。
  • 别把名字搞混了...如果它是一个X、Y点,那么就这样称呼它--而不是一个对象(这是一个非常通用概念)。
  • 你需要调用函数…在对printf()调用中,您使用了point.getXCoordinate-也就是说,您获取了它的地址并要求printf()将其显示为float
  • 你可能会开始怀疑为什么你会关心调用一个函数来访问一个透明结构中的变量。请参见下文。

许多库/API提供不透明的数据库。这意味着你可以得到一个“处理”的“东西".但你不知道“东西”里储存了什么然后,该库为您提供访问函数,如下所示。我建议你这样处理。
别忘了释放内存!
我在下面实现了一个例子。

point.h

#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 */

point.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);
}

main.c

#include <stdio.h>

#include "point.h"

int main(void) {
    struct point *point;

    point = point_alloc();

    point_setCoordinates(point, 1, 2);

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

    point_free(point);

    return 0;
}
edqdpe6u

edqdpe6u2#

你的代码有一些小错误。这就是为什么它不编译。
固定在这里:

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));
}

至于方法,当你可以直接调用它们时,可能不需要在结构体中存储指向方法的指针:

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

ct3nt3jp3#

我也有一个C中的基本类仿真的例子[为特定应用程序指定的OP,尽管,这个答案是针对一般问题的]:
一个名为“c_class. h”的头文件

#ifndef CLASS_HEADER_H
#define CLASS_HEADER_H

// Function pointer prototypes used by these classes
typedef int sub_func_t (int);
typedef float sub_funcf_t (int,int);

/* class type definition 
  (emulated class type definition; C doesn't really have class types) */
typedef struct {
    //Data Variables
    int a;

    /*Function (also known as Method) pointers
      (note that different functions have the same function pointer prototype)*/
    sub_func_t* add;
    sub_func_t* subt;
    sub_func_t* mult;
    sub_funcf_t* div;  
} class_name;

// class init prototypes
// These inits connect the function pointers to specific functions
// and initialize the variables.
class_name* class_init_ptr (int, sub_func_t*, sub_func_t*, sub_func_t*, sub_funcf_t*);
class_name class_init (int, sub_func_t*, sub_func_t*, sub_func_t*, sub_funcf_t*);

#endif

名为“c_class. c”的源代码文件

//gcc -o c_class c_class.c

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"c_class.h"

// The class function definitions.

/*
    If we make these member functions static then they are only 
    accessible via code from this file.
    However, we can still pass the class-like objects around a 
    larger program and access their member functions,
    just like in any OO language.
    
    It is possible to emulate inheritance by declaring a class object 
    from the class type definition (I don't touch on these more 
    abstract subjects though, this is only a basic class emulation).
*/
    
static int AddFunc(int num){
    num++;
    return num;
}

static int SubtFunc(int num){
    num--;
    return num;  
}

static int MultFunc(int num){
    num *= num;
    return num;
}

static float DivFunc(int num, int denom){
    float fnum = (float)num / (float)denom;
    return fnum;  
}

// The class init function definitions.
class_name* class_init_ptr (int num, sub_func_t* addition, sub_func_t* subtraction, sub_func_t* multiplication, sub_funcf_t* division) 
{ 
    class_name* new_class = malloc(sizeof(*new_class)); 
    assert(new_class != NULL);
    *new_class = (class_name){num, addition, subtraction, multiplication, division};
    /*We could also just type:
    new_class->a = num;
    new_class->add = addition;
    new_class->subt = subtraction;   
    new_class->mult = multiplication; 
    new_class->div = division;  
    */
    return new_class; 
}

class_name class_init(int num, sub_func_t* addition, sub_func_t* subtraction, sub_func_t* multiplication, sub_funcf_t* division) 
{ 
    class_name new_class; 
    new_class = (class_name){num, addition, subtraction, multiplication, division};
    /* We could also just type:
    new_class.a = num;
    new_class.add = addition;
    new_class.subt = subtraction;   
    new_class.mult = multiplication; 
    new_class.div = division;  
    */
    return new_class; 
}

//Working Function Prototypes
class_name* Working_Function(class_name*);
class_name Working_Function_Two(class_name);

int main(){
    /* It's possible to connect the functions within the init also,
       w/o sending them. */
    class_name *MyClass = class_init_ptr(5, AddFunc, SubtFunc, MultFunc, DivFunc);
    class_name MyOtherClass = class_init(0, AddFunc, SubtFunc, MultFunc, DivFunc);
    
    printf("%i\n",MyClass->add(100));// 101
    
    printf("%i\n",MyClass->subt(100));// 99

    printf("%i\n",MyClass->mult(100));// 10000

    printf("%f\n",MyClass->div(MyClass->a,2)); // 2.5
    
    printf("%i\n",MyClass->mult(MyClass->mult(100))); //100000000

    MyClass = Working_Function(MyClass);
    //This would work also (because we're passing a pointer):
    //Working_Function(MyClass); 
   printf("%i\n",MyClass->a); //a = 5000

    MyOtherClass = Working_Function_Two(MyOtherClass);
    printf("%i\n",MyOtherClass.a); //a = 9999

    MyOtherClass.a = 25;
    Working_Function_Two(MyOtherClass); //pass by value
    printf("%i\n",MyOtherClass.a); //a = 25  (no value change)

    Working_Function(&MyOtherClass); //pass by reference
    printf("%i\n",MyOtherClass.a); //a = 5000 (value changed)

    return 0;
}

//Working Functions
class_name* Working_Function(class_name* PassedClass){
    printf("%i\n",PassedClass->a);// 5, then 25
    printf("%i\n",PassedClass->add(PassedClass->a));// 6, then 26
    PassedClass->a = 5000;
    return PassedClass;
}

class_name Working_Function_Two(class_name PassedClass){
    printf("%i\n",PassedClass.a);// 0, then 25
    printf("%i\n",PassedClass.add(PassedClass.a));// 1, then 26
    PassedClass.a = 9999;
    return PassedClass;
}

/* We're passing emulated class objects and emulated class pointers 
   by reference and value, if everything works it should print this:

101
99
10000
2.500000
100000000
5
6
5000
0
1
9999
25
26
25
25
26
5000

*/
hsgswve4

hsgswve44#

编写需要多态性的伪类的另一种方法是,创建一个单一的虚函数表,并让构造函数或工厂函数设置它。这里有一个假设的例子。(**编辑:**现在是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;  
}

这利用了结构的公共初始子序列可以通过指向它们中任何一个的指针来寻址的保证,并且每个示例只存储一个类开销指针,而不是每个虚函数一个函数指针。您可以使用虚拟表作为变体结构的类标识符。此外,虚拟表不能包含垃圾。虚函数调用需要解引用两个指针而不是一个,但是任何正在使用的类的虚表都极有可能在该高速缓存中。
我也注意到这个界面非常的 backbone 化;拥有一个除了转换回笛卡尔坐标之外什么也不能做极坐标类是愚蠢的,任何这样的实现至少需要某种方法来初始化动态内存。
如果你不需要多态性,请看Attie的更简单的答案。

dgsult0t

dgsult0t5#

是的,你可以使用c struct创建一个“python类”,
下面是我如何实现python float built_in类型:

/*
Float type
*/
typedef struct _Float _Float;
struct _Float {
    long double value;
    bool is_none;
    _Float *(*_Float_add)(_Float *self, _Float *another_float);
};

_Float *_Float_add(_Float *self, _Float *another_float) {
    _Float *new_float_value;
    new_float_value = malloc(sizeof(_Float));

    if (self->is_none || another_float->is_none) {
        new_float_value->value = 0;
        new_float_value->is_none = true;
        return new_float_value;
    } else {
        new_float_value->is_none = false;
        new_float_value->value = self->value + another_float->value;
        return new_float_value;
    }
}

_Float *Float(long double value) {
    _Float *new_float_value;
    new_float_value = malloc(sizeof(_Float));

    new_float_value->is_none = false;
    new_float_value->value = value;

    new_float_value->_Float_add = &_Float_add;

    return new_float_value;
}

int main()
{
    _Float *a_float = Float(3.2);
    printf("%.3Lf\n", a_float->value);

    _Float *another_float = Float(1.8);
    _Float *sum = a_float->_Float_add(a_float, another_float);
    printf("%.3Lf\n", sum->value);
}

它将打印出3.2和5。

相关问题