非常建议先看#0x0 w~

目标

在这一节中,我们希望能够实现类似C++中一样,能一键new和delete一个对象。

同时,我们也希望能构造一个通用的“类”模板,以便规范我们的编写和调用。

对此,我们需要:

  • 搞清楚一个类的原型需要些什么
  • 实现在程序中“注册”一个“类”
  • 成功new一个对象
  • 实现一系列的函数,以便代替操作符进行一个统一的调用

那么,让我们开始吧~

一个类的原型

为了让所有的类看起来相似,同时也是为了更方便的注册一个类,我们需要首先规定一个类必须要存储哪些信息。

对此,我们不妨先来一个结构体存吧~

1
2
3
typedef struct CLASS_T
{
} class;

首先,我们需要知道如果要新建一个对象的话,需要多少的空间。也就是说,我们需要存储一个类的大小。

于是我们加上const size_t type_size;

接下来,为了能统一所有类的构建,我们并不希望生成一个对象的时候,都要去调用这个类的构造函数(如你要一个cat对象,就要调用cat_constructor;一个dog对象就要dog_constructor)。

我们期望的是像C++那样,可以直接new(cat)、new(dog)。对此我们必须要存储该类的构造函数。

但是,我们先停停,想一想这个构造函数应该传入哪些东西!

  1. 应该传入this吗?

    或许你的答案是不用吧,毕竟我们应该让这个构造函数创建存储区,并返回已经建好的指针,而不是自己去创建一片区域吧?

    但是,这里忽略了一个华点!如果一个子类想要调用父类的构造函数的话,那不就GG了喵——

    所以,一个类的存储空间应该在外部声明,然后传this进去!

  2. 应该定义多少个参数合适?

    答案是:都不合适!
    毕竟要是多了的话,勉强还可以NULL处理;要是少了的话……GG——

    所以,为什么不用人见人爱的va_list呢?

    但是,直接...是不行的!(因为如果想要实现类似new的功能的话,就必须在new的内部来传入参数了,但是new函数不知道有几个参数啊!QAQ)

    一个讨巧的方法是:把va_list指针传进去,想到了喵~

类似的,我们也要声明复制构造和析构函数

1
2
3
void (*constructor)(void *_this, va_list *ap_p); // the constructor of an object
void (*destructor)(void *_this); // the distructoe of an object
void (*deep_copy)(void *_this, void *_that); // deep copy an object

接下来……我们再来尝试实现一下它的一些默认函数吧。对此,我们不妨参考一下Python是怎么做的w

在此,我们只定义部分基础的东西,毕竟当前阶段还没必要写太多~

首先,我们为了实现一点点的模板多态性,来一个iter吧~class *iter;(为什么这里的iteration是一个类的原型而非函数呢)

然后我们再来加上大部分的数学运算符ouo

这里有一个需要注意的点!

为了实现多态,我们一般而言的解决方法是:传入一个指针,像这样:void *(*operator_add)(void *_this, void *_that); 这样是很好啦~

但是它只能处理左值,而不能处理右值(悲

那么,有没有什么东西不管什么都能传入呢?没错,va_list

当然,这要求我们完全的信任程序员,毕竟一旦写成…,传入什么参数,传入多少个参数都是只能交给程序员自己控制了~

于是,我们便将函数声明改成这样:void *(*operator_add)(void *_this, va_list *ap_p);

在定义比较运算符的时候可以取个巧:只用定义gt和eq,ge=gt||eq,lt=!(gt||eq),le=!ge

于是,我们现在便可以给出一个完整的函数原型了~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
typedef struct CLASS_T
{

const size_t type_size; // the size to store an object

void (*constructor)(void *_this, va_list *ap_p); // the constructor of an object
void (*destructor)(void *_this); // the distructoe of an object
void (*deep_copy)(void *_this, void *_that); // deep copy an object

void *(*operator_add)(void *_this, va_list *ap_p);
void (*operator_iadd)(void *_this, va_list *ap_p);
void *(*operator_min)(void *_this, va_list *ap_p);
void (*operator_imin)(void *_this, va_list *ap_p);
void *(*operator_mul)(void *_this, va_list *ap_p);
void (*operator_imul)(void *_this, va_list *ap_p);
void *(*operator_div)(void *_this, va_list *ap_p);
void (*operator_idiv)(void *_this, va_list *ap_p);
void *(*operator_lshift)(void *_this, va_list *ap_p);
void *(*operator_ilshift)(void *_this, va_list *ap_p);
void *(*operator_rshift)(void *_this, va_list *ap_p);
void *(*operator_irshift)(void *_this, va_list *ap_p);
void *(*operator_and)(void *_this, va_list *ap_p);
void *(*operator_iand)(void *_this, va_list *ap_p);
void *(*operator_or)(void *_this, va_list *ap_p);
void *(*operator_ior)(void *_this, va_list *ap_p);
void *(*operator_xor)(void *_this, va_list *ap_p);
void *(*operator_ixor)(void *_this, va_list *ap_p);

bool (*operator_gt)(void *_this, va_list *ap_p);
bool (*operator_eq)(void *_this, va_list *ap_p);

class *iter;
} class;

实现new、delete和copy

new

我们期望new应该能这么使用:new(类名,(参数))

而在new函数之内一般要做这么几件事:分配空间,调用构造函数,返回对象指针

于是,之前埋的伏笔终于有用武之地了!对象所需空间和构造函数在函数原型理都有诶——我们只需要简单的把函数原型的结构体就命名为类名就好了诶——

同时,我们也相当于注册了一个类~

在此给出new的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void *new (const class *_class, ...)
{
class_base *res = malloc(_class->type_size);

if (_class->constructor)
{
va_list ap;

va_start(ap, _class);
_class->constructor(res, &ap);
va_end(ap);
}

res->self_class = _class; //伏笔,不用在意是什么现在

return res;
}

delete

对于delete期望这样使用:delete(对象指针)

里面就会自动的调用析构函数了w
但是这里有个小问题:我们传进去的是this指针,而不是函数原型。该怎么找到析构函数呢?

我们当然可以把析构函数拷贝到对象里,也可以通过某种数据结构关联。但或许我们可以把函数原型的指针也存进去不就好了嘛,还能顺便解决运算符调用的问题。

于是我们声明一个基类,并要求所有的类都继承该基类,然后在构造时赋值即可(伏笔回收~)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct CLASS_BASE_T
{
class *self_class;

} class_base;

void delete (void *_this)
{
const class_base *this = _this;

if (this->self_class->destructor)
{
this->self_class->destructor(this);
}

free(this);
}

copy

复制构造。会自动复制this的内容并返回一个新的对象。这里直接给出代码~

1
2
3
4
5
6
7
8
9
10
11
12
void *copy(const void *_this)
{
const class_base *this = _this;

class_base *res = malloc(this->self_class->type_size);

memcpy(res, this, this->self_class->type_size);

this->self_class->deep_copy(this, res);

return res;
}

两个奇怪的函数

由于子类在调用父类的构造、析构函数时。用new是不行的。对此,我们新定义两个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
void constructor(const class *_class, void *_this, va_list *ap_p)
{
const class_base *this = _this;

_class->constructor(this, ap_p);
}

void destructor(class *_class, void *_this)
{
const class_base *this = _this;

_class->destructor(this);
}

统一的运算符

这里的思想其实都是上面用过的,对此我就给出addiadd来抛砖引玉咯~

(你要完整版代码的话,仓库自取~)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void *add(void *_this, ...)
{
const class_base *this = _this;
va_list ap;
va_start(ap, _this);

if (this->self_class->operator_add)
{
return this->self_class->operator_add(_this, &ap);
}

va_end(ap);

return NULL;
}

void iadd(void *_this, ...)
{

const class_base *this = _this;
va_list ap;
va_start(ap, _this);

if (this->self_class->operator_iadd)
{
this->self_class->operator_iadd(_this, &ap);
}

va_end(ap);
}

尾声

希望以上的东西能给你带来启发,要是能学到新的东西就再好不过了~

下一章,我们来尝试实现一个重要的基础类:iterator