了解OOPC,我做得对吗?

时间:2014-03-22 17:43:36

标签: c oop

我使用ansi c关注了Alex关于面向对象编程的书。

到目前为止,我们试图模拟一个非常基本的字符串类 -

以下是代码:

的main.c

#include <stdio.h>
#include <stdlib.h>
#include "class.h"
#include "mystring.h"

extern const void *String_c;

int main() {
        String *my = new(String_c, "A random string");
        char *text = my->str(my);
        printf("String contains %s of length %d", text, my->length(my));
        delete(my);
        free(text);
        return 0;
}

class.h

#ifndef CLASS_H_
#define CLASS_H_

#include <stddef.h>

typedef struct {
        size_t size;
        void* (*ctor) (void* self, va_list *app);
        void (*dtor) (void* self);
} Class;

void* new(const void *class, ...);
void delete(void *object);

#endif /* CLASS_H_ */

class.c

/*
 * class.c
 *
 *  Created on: 22-Mar-2014
 *      Author: nilesh
 */

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

#include "class.h"

void *new(const void *_class, ...) {
        printf("\nCreating new\n");
        const Class *class = _class;
        void *p = calloc(1, class->size);
        assert(p);
        * (const Class **) p = class;
        if(class->ctor) {
                va_list ap;
                va_start(ap, _class);
                p = class->ctor(p, &ap);
                va_end(ap);
        }
        return p;
}

void delete(void *object) {
        printf("\nDelete\n");
        const Class **class = object;
        if(object && *class && (*class)->dtor)
                (*class)->dtor(object);
        free(object);
        object = NULL;
}

mystring.h

#ifndef STRING_H_
#define STRING_H_

#include <stddef.h>

#include "class.h"

typedef struct string String;

struct _string;

struct string {
        const Class *class;
        struct _string *_;
        int (*length) (String *self);
        char* (*str) (String *self);
};

extern Class _string_class;
extern const void *String_c;

#endif /* STRING_H_ */

mystring.c

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

#include "mystring.h"

struct _string {
        char *data;
        int length;
};

static int length(String *self) {
        return self->_->length;
}

static char* str(String *self) {
        char *ret = malloc(sizeof(char) * self->_->length);
        memcpy(ret, self->_->data, sizeof(char)*self->_->length);
        return ret;
}

void* ctor(void *_self, va_list *app) {
        printf("\nConstructor called\n");
        String *self = _self;
        self->_ = malloc(sizeof(struct _string));
        char *text = va_arg(*app, char *);
        self->_->length = strlen(text);
        self->_->data  = malloc(sizeof(char) * self->_->length);
        memcpy(self->_->data, text, sizeof(char) * self->_->length);

        self->length = length;
        self->str = str;

        return self;
}

void dtor(void *_self) {
        printf("\nDestructor called\n");
        String *self = _self;
        free(self->_);
        free(self->_->data);
        self->_->data = NULL;
}

Class _string_class = {sizeof(String), ctor, dtor};
const void *String_c = &_string_class;

我有一个问题:

为什么

Class *class = object 

删除不起作用,而

Class **class = object

作品?

从某种意义上讲,在前者中它不会调用dtor,但会调用长度,如果是后者,函数调用是(*class)->dtor可以工作。

1 个答案:

答案 0 :(得分:3)

简短回答是class的类型为Class **; class->dtor仅在class类型为Class *时才有效。

由于双重间接,你可能会感到困惑,所以这里有一个更长的解释:

考虑结构布局。想象一下,你有一个像这样简单的结构:

struct example {
    int xpto;
    char a[10];
}

如果您调用函数f()并将指针p传递给struct example,则f()可以自由地将此指针投射到int *。取消引用此类指针会产生与p->xpto相同的结果。也就是说,p->xpto*(int *) p是等价的。发生这种情况是因为结构组件在增加的内存地址中布局。 xpto是第一个成员,意味着它位于偏移0处。换句话说,对于任何指向struct example的指针,sizeof(int)指向的地址处的第一个p字节属于到xpto

您的字符串结构定义为:

struct string {
        const Class *class;
        struct _string *_;
        int (*length) (String *self);
        char* (*str) (String *self);
};

这表明在struct string的偏移0处有一个(只读)指向Class的指针。当您在delete(my)中调用main()时,您将指向struct string - 因此,sizeof(const Class *)指向的地址中的第一个my字节是指向Class的指针。就像我们在struct example示例中所做的那样 - 我们将p转换为指向第一个成员的指针 - 将指针转换为Class **(第一个成员是Class *,所以指向第一个成员的指针是类型Class **),可以直接访问第一个字段(只有第一个字段)。

因此,delete()会将您指定的指针强制转换为Class **,因为通过这样做,取消引用这样的指针会产生Class *

为什么class->dtor()不起作用?由于class的类型为Class **,因此class->dtor相当于(*class).dtor无效:*class的类型为Class *,它是不是结构,因此,没有名为dtor的成员。您必须使用(*class)->dtor,因为它与(*(*class)).dtor相同。