关于卢彦的"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
class Base  {
{  private int m=2;
 private int m=2;  }
}  class Child : Base
class Child : Base  {
{ private int m=1;
 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;
using System;  public class Base
public class Base  {
{  public Base innerBase;
 public Base innerBase; private int m=2;
 private int m=2;  public void Call()
 public void Call() {
 { //此处可以访问innerBase.m,因为安全上下文是Base
 //此处可以访问innerBase.m,因为安全上下文是Base Console.WriteLine(innerBase.m);
 Console.WriteLine(innerBase.m); }
 } }
}  class test
class test  {
{  public static void Main()
 public static void Main()  {
 {  Base b = new Base();
 Base b = new Base();  b.innerBase = b;
 b.innerBase = b; // 此处无法访问b.innerBase.m,因为安全上下文是test
 // 此处无法访问b.innerBase.m,因为安全上下文是test b.Call();
 b.Call(); }
 }  }
}
我们再来研究另外一个更为复杂的例子(根据“关于protected的Quiz ”的一个评论改编的): using System;
using System;  class Base
class Base  {
{  private int m=2;
 private int m=2;  public int GetM(Child c)
 public int GetM(Child c) {
 { return c.m;
 return c.m; }
 }
 public int GetM(Base b)
 public int GetM(Base b) {
 { return b.m;
 return b.m; }
 }
 public Base()
 public Base() {}
 {}
 public Base(int m)
 public Base(int m) { this.m = m; }
 { this.m = m; } }
} 
 class Child : Base
class Child : Base  {
{ private int m=1;
 private int m=1; public new int GetM(Child c)
 public new int GetM(Child c) {
 { return c.m;
 return c.m; }
 }
 public Child(int m):base(m)
 public Child(int m):base(m) {this.m = 4;}
 {this.m = 4;} }
} 
 class test
class test  {
{  public static void Main()
 public static void Main()  {
 { 
 Base b = new Base();
 Base b = new Base();  Child c = new Child(3);
 Child c = new Child(3);  Console.WriteLine(b.GetM(c));
 Console.WriteLine(b.GetM(c));  Console.WriteLine(c.GetM(c));
 Console.WriteLine(c.GetM(c)); Console.WriteLine(b.GetM((Base)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是怎么回事。这些将在以后的文章中给予论述。
注:以上内容纯粹个人观点,具体实现我没能找到更多的相关资料,而且编译器的实现方法各不相同。
 


 
  
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号