C中的OOP,隐式传递self作为参数

C中的OOP,隐式传递self作为参数,c,oop,C,Oop,我一直在编写一个示例来学习C中的OOP。目前,我已经编写了这段代码,这段代码正在运行,但是我对使这些方法隐式地将self作为参数传递很感兴趣 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> //#include "Stopwatch.h" typedef struct stopwatch_s { unsigned int milliseconds; unsigned i

我一直在编写一个示例来学习C中的OOP。目前,我已经编写了这段代码,这段代码正在运行,但是我对使这些方法隐式地将self作为参数传递很感兴趣

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
//#include "Stopwatch.h"


typedef struct stopwatch_s
{
    unsigned int milliseconds;
    unsigned int seconds;
    unsigned int minutes;
    unsigned int hours;

    bool is_enabled;

    void ( *tick )      ( struct stopwatch_s* );
    void ( *start )     ( struct stopwatch_s* );
    void ( *stop )      ( struct stopwatch_s* );
    void ( *reset )     ( struct stopwatch_s* );
} stopwatch_t;

static void tick (stopwatch_t* _self)
{
    stopwatch_t * self = _self;
    if (self->is_enabled)
    {
        self->milliseconds++;
        if (self->milliseconds >= 1000)
        {
            self->milliseconds = 0;
            self->seconds++;

            if (self->seconds >= 60)
            {
                self->seconds = 0;
                self->minutes++;

                if (self->minutes >= 60)
                {
                    self->minutes = 0;
                    self->hours++;
                }
            }
        }
    }
}

static void start (stopwatch_t* _self)
{
    stopwatch_t * self = _self;
    self->is_enabled = true;
}

static void stop (stopwatch_t* _self)
{
    stopwatch_t * self = _self;
    self->is_enabled = false;
}

static void reset (stopwatch_t* _self)
{
    stopwatch_t * self = _self;
    self->is_enabled = false;
    self->milliseconds = 0;
    self->seconds = 0;
    self->minutes = 0;
    self->hours = 0;
}

void * new_stopwatch()
{
    stopwatch_t * newInstance = (stopwatch_t *)calloc(1, sizeof(stopwatch_t));
    newInstance->is_enabled = false;
    newInstance->milliseconds = 0;
    newInstance->seconds = 0;
    newInstance->minutes = 0;
    newInstance->hours = 0;
    newInstance->tick = &tick;
    newInstance->start = &start;
    newInstance->stop = &stop;
    newInstance->reset = &reset;

    return newInstance;
}

void main()
{
    struct stopwatch_s * Stopwatch = new_stopwatch();
    printf ("Initial: %d\n", Stopwatch->milliseconds);
    Stopwatch->start (Stopwatch);
    Stopwatch->tick (Stopwatch);
    Stopwatch->tick (Stopwatch);
    Stopwatch->tick (Stopwatch);
    printf ("Started: %d\n", Stopwatch->milliseconds);
    Stopwatch->stop (Stopwatch);
    Stopwatch->tick (Stopwatch);
    printf ("Stopped: %d\n", Stopwatch->milliseconds);
    Stopwatch->reset (Stopwatch);
    printf ("Reset: %d\n", Stopwatch->milliseconds);    
}
我会写字

Stopwatch->tick();

这是SimPy,不是C语言的特征,这可能是发明C++的动机之一,所以单独使用C,这是不可能的。 C语言中有很多库,我所知道的基本上都使用与您相同的方法,使用结构来存储状态(在您的例子中,

stopwatch\t
),但只要尽可能跳过结构中的函数指针即可;这不是因为C程序员不喜欢OOP,而是因为OOP的冗余度较低

 stoplib_tick(Stopwatch);
而不是

Stopwatch->tick(Stopwatch);
 Stopwatch->tick(Stopwatch);
另外,在结构的每个实例中携带一堆始终相同的函数指针是个坏主意;这只是浪费空间,而且可能导致错误。如果需要,可以创建一个包含类型的所有函数指针的结构,并从该表中调用它们。基本上,C++中的VTables就是这样。 因此,没有C程序员会这样做;除非您的函数指针实际上可能会发生更改,否则您不能将可以对结构执行的操作保留在该结构中

我不知道你指的那本书,但如果它鼓吹这样做,我不喜欢它

严重的,如果你想要C对象定位,去C++;除了编码几个内核之外,你可以用C++做的事情很少,C可以用C来实现。不要像20世纪60年代那样

EDIT开始阅读你链接到的PDF——说真的,现在谁会使用ANSI-C特别是如果你想舒适地使用结构等,你不应该使用比C99更旧的东西(考虑到它已经很旧了…),因此,这本书已经过时了,除非你提出了一个非常重要和传统的系统(“嗨,我从20世纪80年代就开始研究核武器控制系统,我需要修复这个和那个”),我想说,我想不出有什么情况下遵循这些例子是有意义的;显然,“我正在从头开始学习用C语言进行OOP”不应该基于已经过时十多年的东西

编辑:您可以发表评论


因为我来自一个嵌入式环境,坚持ANSI-C,一切都会正常工作

我有点勉强同意。有些平台上缺乏C99支持,但是:大多数编译器支持C99的绝大多数功能。ANSI-C(应该更准确地称为C89,因为C99也是ANSI标准)现在已经超过25岁了,在不知道的情况下,你的代码甚至可能不符合C89。书中的代码肯定不是有效的ANSI-C,不管作者怎么说。例如,ANSI-C没有
/
注释;这只是一个小错误,我猜所有的编译器,除非设置为pedantic m奥德不会抱怨,但这仍然不是一个好的景象

所以:帮你自己一个忙,不要依赖一本选择性地使用难用语言状态的书,而试着使用编译器支持的任何东西

另外:我越是读那本书,在(现代)OOP中的一个好练习就越少(第3页,PDF第9页):

通用指针
void*
贯穿始终。一方面,它使 不可能发现一个集合是什么样子的,但另一方面它允许我们 将几乎所有内容传递给
add()
和其他函数

是的,因为类型安全性对C语言的成功不是一个至关重要的概念,而且因为传递
void*
然后,在方法的第一行,将其转换为所需的指针类型是一个好主意。啊!如果你想要可怕的bug,那就是得到它们的方法

看看像CPython这样的东西:Python是一种OO语言,但是解释器/编译器是用C编写的。Python使用PyObject结构进行C-OOP,作为主要功能,PyObject结构有一个类型引用,以避免执行这些盲转换。您不应该能够传递
const char[]
在这里,您希望有一个指向某个对象的指针,多态性的全部要点是,您可以使用类型的子类型,但不能完全不同的类型,并使用函数。这本书对OOP没有任何好处。请阅读其他内容。我很确定有关于CPython设计的书籍,我个人认为再糟糕不过了

我不知道如何构造我的“对象”,所以

Stopwatch->tick(秒表);

我可以写
Stopwatch->tick();

<>这在标准C中是不可能的。你需要把接收方作为一个显式形式的参数传递给C函数(与C++相比,它有<代码>这个< /C> >作为隐式形式)。 然而:

  • 通常,您希望将所有方法函数打包到一个包含多个函数成员的
    结构中(并使每个实例以指向该
    结构的指针开始)。请阅读-s

  • 您可以使用一些宏(或者内联函数)来避免给出两次
    Stopwatch
    ;您仍然可以编写
    TICK(Stopwatch)
    而不是
    Stopwatch->TICK();
    ;的扩展可能很有用

以GTK和它作为C语言的可爱对象系统的一个例子。请阅读上的和Wiki页面。可能会看到报告和

顺便说一句,你可以决定你有第一类的方法选择器(可能是整数,或者指向一些常见选择器类型的指针),然后编码一个可变的
send
函数(这样你就可以编码
send(秒表,滴答声)
而不是你梦寐以求的
StopWatch->TICK()
)或宏
typedef struct stopwatch_t stopwatch_t; // incomplete type

stopwatch_t* sw_new (void);             // "constructor"

void sw_delete (stopwatch_t* sw);       // "destructor"

void sw_tick (const stopwatch_t* sw);   // public member function
// any number of public functions here
// mind const correctness!
struct stopwatch_t        // implementation
{
  // true private variables:

  unsigned int milliseconds;
  unsigned int seconds;
  unsigned int minutes;
  unsigned int hours;
  bool is_enabled;
};

stopwatch_t* sw_new (void)
{
  // same as what you already have
}

// the module is responsible for cleaning up its own mess, NOT THE CALLER
void sw_delete (stopwatch_t* sw)
{
  free(sw);
}

// any number of public member functions:
void sw_tick (const stopwatch_t* sw)
{
  // here sw is the "self"/"this" pointer
}

// any number of private member functions:
static void sw_do_stuff (stopwatch_t* sw)
{
}
// stop_watch.h
typedef struct stop_swatch_ stop_watch;

stop_watch* stop_watch_create();
stop_watch* stop_watch_clone(stop_watch const* sw);
void stop_watch_dispose(stop_watch* sw);

void stop_watch_tick(stop_watch* sw);
void stop_watch_start(stop_watch* sw);
void stop_watch_stop(stop_watch* sw);
void stop_watch_reset(stop_watch* sw);
// stop_watch.h
typedef struct stop_watch_functions_ stop_watch_functions;

typedef struct {
    stop_watch_functions const* functions;
} stop_watch;

struct stop_watch_functions_ {
    void (*clone)(stop_watch const*);
    void (*dispose)(stop_watch*);

    void (*tick)(stop_watch*);
    void (*start)(stop_watch*);
    void (*stop)(stop_watch*);
    void (*reset)(stop_watch*);
};

stop_watch* stop_watch_clone(stop_watch const* sw);
void stop_watch_dispose(stop_watch* sw);

void stop_watch_tick(stop_watch* sw);
void stop_watch_start(stop_watch* sw);
void stop_watch_stop(stop_watch* sw);
void stop_watch_reset(stop_watch* sw);
// stop_watch.c
stop_watch* stop_watch_clone(stop_watch const* sw) {
    return (*sw->functions->clone)(sw);
}

void stop_watch_dispose(stop_watch* sw) {
    return (*sw->functions->dispose)(sw);
}

void stop_watch_tick(stop_watch* sw) {
    return (*sw->functions->tick)(sw);
}

void stop_watch_start(stop_watch* sw) {
    return (*sw->functions->start)(sw);
}

void stop_watch_stop(stop_watch* sw)  {
    return (*sw->functions->stop)(sw);
}

void stop_watch_reset(stop_watch* sw) {
    return (*sw->functions->reset)(sw);
}
// my_stop_watch.h
#include "stop_watch.h"

typedef struct my_stop_watch_ my_stop_watch;

my_stop_watch* my_stop_watch_create();

stop_watch* my_stop_watch_upcast(my_stop_watch* msw);
my_stop_watch* my_stop_watch_downcast(stop_watch* sw);
// my_stop_watch.c
#include "my_stop_watch.h"

struct my_stop_watch_ {
    stop_watch base;

    unsigned int milliseconds;
    unsigned int seconds;
    unsigned int minutes;
    unsigned int hours;

    bool is_enabled;
};

static stop_watch* my_stop_watch_clone(stop_watch const* sw) {
    my_stop_watch* new = malloc(sizeof(my_stop_watch));
    memset(new, (my_stop_watch const*)sw, sizeof(my_stop_watch));
}

static void my_stop_watch_dispose(stop_watch* sw) {
    free(sw);
}

static void my_stop_watch_tick(stop_watch* sw) {
    my_stop_watch* msw = (my_stop_watch*)sw;
    /* do something */
}

static void my_stop_watch_start(stop_watch* sw) {
    my_stop_watch* msw = (my_stop_watch*)sw;
    /* do something */
}

static void my_stop_watch_stop(stop_watch* sw) {
    my_stop_watch* msw = (my_stop_watch*)sw;
    /* do something */
}

static void my_stop_watch_reset(stop_watch* sw) {
    my_stop_watch* msw = (my_stop_watch*)sw;
    /* do something */
}

static stop_watch_functions const my_stop_watch_table = {
    &my_stop_watch_clone,
    &my_stop_watch_dispose,

    &my_stop_watch_tick,
    &my_stop_watch_start,
    &my_stop_watch_stop,
    &my_stop_watch_reset
};

my_stop_watch* my_stop_watch_create() {
    my_stop_watch* msw = malloc(sizeof(my_stop_watch*));

    msw->base = &my_stop_watch_table;

    /* do something */

    return msw;
}

stop_watch* my_stop_watch_upcast(my_stop_watch* msw) {
    return &msw->base;
}

my_stop_watch* my_stop_watch_downcast(stop_watch* sw) {
    if (sw->functions != &my_stop_watch_table) {
        return NULL;
    }

    return (my_stop_watch*)((char*)sw - offsetof(my_stop_watch, base));
}