返回介绍

4.7 子类的实现 - 环

发布于 2025-02-24 22:44:38 字数 2657 浏览 0 评论 0 收藏 0

我们已经做好了些完整实现的准备,我们可以选择先前部分介绍的我们最喜欢的技术。面向对象规定我们需要一个构造器,可能的话还会有一个析构器, Circle_draw() ,和类型描述 Circle 都绑定在一起。以便于练习我们的方法,我们包含了 Circle.h 并增加了下面的行在 4.1 部分的程序中做测试:

case 'c':
     p=new(Circle,1,2,3);
     break;

现在我们能够观察到下面的测试程序的表现:

$ circles p c
"." at 1,2
"." at 11,12
circle at 1,2 rad 3
circle at 11,22 rad 3

环的构造函数接收 3 个参数:第一个参数为环的点的坐标接下来是半径。初始化点部分是点的构造器的工作。它会处理部分 new() 参数列表的参数。环的构造器从它的初始化半径的地方携带保留的参数列表。

一个子类的构造器首先应该允许超类做部分初始化,这部分初始化把清晰地内存带进超类对象。一旦超类构造器构造完成,子类构造器完成初始化并把超类对象带进子类对象中。

对于环,意味着我们需要调用 Point_ctor() 。像其他所有动态链接一样,这个函数被声明为 static ,因此隐藏在 Point.c 的内部。然而,我们仍然能够通过在 Circle.c 中可用的类型描述符来 Point 获得此函数。

static void * Circle_ctor (void * _self, va_list * app)
{
     struct Circle * self =
     ((const struct Class *) Point) —> ctor(_self, app);

     self —> rad = va_arg(* app, int);
     return self;
}

这里应该很清楚为什么我们传递参数的地址 app 列表指针到每个构造器而不是 va_list 的值本身: new() 调用子类的构造器,此构造器调用超类的构造器,等等。最超级的构造器是第一个将去实际的作一些事情,并且会捡起传进 new() 的最左边的参数列表。保留的参数对于下一个子类是可用的,等等知道最后,最右边的参数被最终的子类所使用,也就是说,被 new() 所直接的调用的构造器所调用。

构造器以严格的相反的次序是最好的组织: delete() 调用子类的析构器。它首先应该销毁它自己的资源接下来调用直接的超类的析构器,这个析构器可直接的销毁下一个资源集等等。构造是先发生在子类之前的父类上的。析构则是相反,子类要先于父类,即,环部分要先于点部分。这里,然而,什么也不需要做。

我们先前已经让 Circle_draw() 工作了,我们使用可见部分,并且编码表示文件 Point.r 如下:

struct Point {
     const void * class;
     int x, y; /* coordinates */
};
#define x(p) (((const struct Point *)(p)) -> x)
#define y(p) (((const struct Point *)(p)) -> y)

现在我们可以对于 Circle_draw() 使用访问宏:

static void Circle_draw (const void * _self)
{
    const struct Circle * self = _self;
     printf("circle at %d,%d rad %d\n",x(self), y(self), self —> rad);
}

move() 拥有静态链接并且被从点的实现上继承。我们得出结论环的实现是通过定义仅仅全局可见 Circle.c 的部分内容:

static const struct Class _Circle = {
     sizeof(struct Circle), Circle_ctor, 0, Circle_draw
};

const void * Circle = & _Circle;

然而,在接口,表示式,实现文件之间似乎我们有一个可行的分配程序文本实现类的策略,点和环的例子还没有显现出一个问题:如果一个动态连接的方法如 Point_draw() 在子类中没有被重写,子类的类型描述符需要指向在父类实现的函数。函数名,然而在这里被定义成 static ,因此选择器是不能够被规避的。我们将在第六章看到一个清晰地解决此问题的方法。作为暂时的权衡,我们在这种情况下可以避免对 static 的使用,仅仅在子类的实现文件中声明函数的头,对于子类并且使用函数名去初始化类型描述。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文