本篇来尝试描述一下类如何实现。

类是面向对象编程的基础, 即使不实现一个完全的类, 也应该实现一个可以储存复合数据的结构。

一般情况下,都会将类实例化出来的东西叫做对象。 关于面向对象的思想,不属于本篇的讨论范围。

本篇所讨论的仅仅是基本的实现方式, 关于如何优化内存的内容,暂时不讨论。

基本内容

langX 代码示例

// 声明一个类
Student {
  name;
  age = 10 ;

  Student => (name,age){
    this.name = name;
    this.age = age;
    printInfo("in ctor.");
  }

  a => { print("hello,a!\n");}

  printInfo => (a3){ print("name: " + name + "\n");  print("age: " + age + "\n" ); print("a3: " + a3 + "\n");}

}

// 声明一个对象
s2 = new Student("hideDragon",20);
b = s2.age;
c = s2.name;
print("b: " + b + "\n");
print("c: " + c + "\n");
print("s2.age: " + s2.age + "\n");

在 langX 里, 笔者使用类名 { 字段列表 和 函数列表 } 的形式来声明一个类。 读者可以设计自己喜欢的语法。

这里说一点题外话, 笔者省略 class 或其他关键字的想法是为了节省没必要的关键字。 但是现在仔细想想,虽然确实省下了关键字, 但是却增加模糊性,使代码不太好辨别。不论是阅读还是解析上。

主要思路

将类的 字段列表, 函数列表都保存下来即可。

在类实例化的时候, 需要复制字段列表, 但是不必复制函数列表, 使用一个指针指向类信息, 或者使用函数对象的指针也行。

如何实现函数,已经在 6.1节中有所介绍。

字段信息, 大致有下面的内容:

  • 访问修饰符 (可省略)
  • 字段名称
  • 字段类型 (弱类型语言可省略)
  • 字段的默认值

其中字段名称, 如果考虑在编译的时候全部转成索引,且不实现运行时的反射的话, 可以考虑不保存。

不过,脚本语言一般情况下还是需要保存字段名称, 在动态加载新的脚本的时候可能会用到。

在生成对象之后, 还需要调用类的构造函数。 如果实现了析构函数的话, 在类的内存被释放之前还应该调用类的析构函数。

关于类函数调用的内容将在后续章节讲解。

总结

在 c, lua 和 js 里面好像没有继承的观念, 在 java/c# 里面是单继承, 但是可以实现多个接口。 在 cpp 里面好像是没有接口的观念, 但是可以多继承。

笔者猜测: 在 c里面应该可以使用 宏,或者内存布局等术来模拟继承。

无论他们对待继承的态度如何, 他们都实现了复合数据的结构, 所以读者在实现自己的脚本语言的时候, 也应该提供一个类似的机制。

此外,与函数类似, 类也比较像一个包装。