First we try, then we trust

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

关于卢彦的"protected的Quiz"我想在这里再写点什么。尽管一直想写个“闲聊面向对象程序”系列文章,但总没有抽出足够的时间,所以先半截腰谈谈我对面向对象的认识。

面向对象绝对是一个不好学的课程,李维的《深入核心VCL架构剖析》给了我不少启示。面向对象之所以具有继承、封装、多态等特征,其本质与VTM(Virtual Method Table)有直接关系。而不同语言编译器在实现上采用的技术也各不相同。下面是某系统的一个对象在内存中的布局

Offset  What 
+0  Pointer to VMT.
+4  Data. All fields in the order the've been declared. 
...  

今天不讨论VTM,先说说Data。假设有如下两个类
class Base
{
private int m=2;
}

class Child : Base
{
private int m=1;
}

它们经过编译后,执行以下两条命令:

Base b = new Base();
Child c = new Child();

形成的内存结构(猜测,希望大家多指正)如下(图里省略了VTM,仅仅关注数据部分,下同):

由图中我们可以看到,c中包含了两个m(没错,所有父类的字段,不管是私有还是公有都会在子类中得到继承),一个是继承下来的,一个是自身的m。只不过从存取权限上讲,c只能存取自己的那个m,而无法访问父类继承的那个m。

然后我们再来讨论安全性问题。C#语言为我们提供了private、public、protected三种访问控制符,关于它们的区别就不用我多说了。就我的感觉,对象的每一个方法、字段、属性都具有一个安全上下文,当这个对象试图访问另外一个对象的内容时,需要出示自己的安全上下文,在被允许的情况下才可以访问。看一个例子:

using System;
public class Base
{
public Base innerBase;
private int m=2;
public void Call()
{
//此处可以访问innerBase.m,因为安全上下文是Base
Console.WriteLine(innerBase.m);
}

}

class test
{
public static void Main()
{
Base b
= new Base();
b.innerBase
= b;
// 此处无法访问b.innerBase.m,因为安全上下文是test
b.Call();
}

}



我们再来研究另外一个更为复杂的例子(根据“关于protected的Quiz ”的一个评论改编的):

using System;
class Base
{
private int m=2;
public int GetM(Child c)
{
return c.m;
}


public int GetM(Base b)
{
return b.m;
}


public Base()
{}

public Base(int m)
{ this.m = m; }
}


class Child : Base
{
private int m=1;
public new int GetM(Child c)
{
return c.m;
}


public Child(int m):base(m)
{this.m = 4;}
}


class test
{
public static void Main()
{

Base b
= new Base();
Child c
= new Child(3);
Console.WriteLine(b.GetM(c));
Console.WriteLine(c.GetM(c));
Console.WriteLine(b.GetM((Base)c));
}

}


结果是3,4,3

当执行Child c = new Child(3);命令时,系统将调用构造函数:
 public Child(int m):base(m)
 {this.m = 4;}
构造函数首先调用基类Base的构造函数,于是基类中的m被初始化成3,然后对Child的m进行赋值,Child中的m变成了4,经过构造函数构造后,内存布局如下图:



然后,我们看看为什么Console.WriteLine(b.GetM(c));的结果是3。在Main中,我们是无法访问到c.m的,因为m是私有变量。但在Base的GetM中我们却访问到了值,而且是3,这是为什么?如下图



至于后面两条命令的返回结果为什么是4与3留给读者自己考虑吧。

  Console.WriteLine(c.GetM(c));
  Console.WriteLine(b.GetM((Base)c));

这道题中,如果将Child中的 m 声明改成 public int m = 1; 程序的运行结果是 4,4,3,为什么这样,也留给读者考虑吧。

实际上,有很多隐含的东西在这里不便多说,比如对象的类型转换是怎么回事,多态性在编译过程中的实现过程什么样的,静态与动态的本质区别又是什么,VTM是怎么回事。这些将在以后的文章中给予论述。

注:以上内容纯粹个人观点,具体实现我没能找到更多的相关资料,而且编译器的实现方法各不相同。

posted on 2004-06-29 00:06  吕震宇  阅读(4857)  评论(11编辑  收藏  举报