我使用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
可以工作。
答案 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
相同。