posts - 129, comments - 17, trackbacks - 0, articles - 0
  IT博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

语言记忆

Posted on 2005-08-04 19:43 woow 阅读(131) 评论(0)  编辑 收藏 引用
C#中虛函數,抽象,接口的簡單説明
虛函數:由virtual聲明,它允許在派生類中被重寫,要重寫方法,必須先聲名為virtual
public class myclass
{
public virtual int myint()
{
函數体;
}
}
class myclass1:myclass
{
public override int myint()
{
函數体1;
}
}

抽象類、抽象函數:由abstract聲明,在抽象類中可以定義抽象方法,抽象方法基本沒有執行代碼,派生類必須重寫它,提供其執行代碼
public abstract class myclass
{
public abstract int myint();
}
public class myclass1:myclass
{
public override int myint()
{
函數体;
}
}

接口類:由interface聲明,是特殊的抽象類,是方法、屬性、事件和索引符的組合,沒有字段,其成員無執行方式,無構造函數,不允許進行運算符重載,接口和它的成員沒有任何訪問修飾符,它總是公共的,不能聲明為虛擬或靜態,繼承自接口的派生類必須實現接口中的所有方法
interface Imyinterface
{
void myfunction();
string name
{
get;
set;
}
}
class myclass:Imyinterface
{
void myfunction()
{
函數体;
}
string name
{
get
{
return name;
}
set
{
name=value;
}
}
}
C++和C#构造函数的区别


(1)C++ 不允许在一个构造函数中调用另外一个构造函数(称为委派构造函数调用),而 C# 则允许。例如:
         C++:


  struct Point {
  public:
  int X, Y;
  Point(int x, int y);
  Point(Point pt) : Point(pt.X, pt.Y) { } // 错误,C++ 不允许
  };

  C#:


  struct Point {
  public int X, Y;
  public Point(int x, int y);
  public Point(Point pt) : Point(pt.X, pt.Y) { } // 可以,C# 允许
  };

  委派构造函数调用语法上非常自然和易懂,因此你也许会质疑 C++ 不提供它是不是故意给程序员添麻烦。事实上,C++ 不提供这一特性并不是出于语法上的考虑,而是出于资源管理的考虑(噢,这样的事情对 C++ 来说还有很多很多)。

  我们知道,C++ 的构造函数用于分配资源,而析构函数用于释放资源,构造函数和析构函数调用必须匹配,否则就打破了 C++ 的基本规则。

  如果允许委派构造函数调用,则显然会打破这一规则——构造函数被执行两次,而析构函数只执行一次。当然,对一些类,例如前面的那个 Point 来说这不是个问题,但是从语言机制的角度讲这个特性可能属于“危险”的特性。注:在最新的 C++ 标准提议草案中,Herb 等人有一个关于允许委派构造函数调用的提案,当然这很大程度上是为了方便 C++/CLI 绑定。

  (2)在 C++ 构造函数中,虚函数调用会被编译器自动转为普通函数调用,而在 C# 构造函数中允许进行虚函数调用。C++ 这样处理自然有它的原因——在 C++ 中,构造函数执行完成后对象才初始化好,对于多态对象来说,也就意味着构造函数在背后执行了很重要的一件事情——初始化对象的虚函数表。

  如果我们在基类的构造函数中调用了虚函数,则因为此时对象的虚函数表仍旧是基类的虚函数表,所以无法进行正确的虚函数调用。也就是这个原因,通常我们应该避免在构造函数中调用虚函数,因为它违背了虚函数的语义。而在 C# 中,在对象的构造函数执行之前对象的类型信息就已经初始化好了,所以可以进行正常的虚函数调用。
                                                                           虚函数的另类解释

虚拟函数(virtual)
  使用面向对象的开发过程就是在不断的抽象事物的过程,我们的目标就是抽象出一个紧内聚,低偶合,易于维护和扩展的模型.但是在抽象过程中我们会发现很多事物的特征不清楚,或者很容易发生变动,怎么办呢?比如飞禽都有飞这个动作,但是对于不同的鸟类它的飞的动作动方式是不同的,有的要滑行,有的要颤抖翅膀,虽然都是飞但千差万别,在我们抽象的模型中不可能一个个都考虑到,怎么为以后留下好的扩展,怎么来处理子类的前差万别?比如我现在又要抽象一个类"鹤",它也有飞禽的特征,我可以简单的继承"飞禽",而不去修改现有的代码,可以很容易的扩展系统
  面向对象的概念中引入了虚拟函数. 就是在父类中把子类中共同的,易于变化或者不清楚的特征抽取出来,作为子类需要去重新实现的操作(override),我们可以称做"热点".还是上面的例子
  class 飞禽
  {  
    private void Shape ;    //注意private访问修订符,Shape是不会被子类继承的!
    public string Wing ;    //翅膀
    public string Feather ; //羽毛   
    public virtual boolean Fly() {} ;  //飞翔. 定义的虚拟函数, 这是一个热点
  }
  
  class 麻雀 : 飞禽    //麻雀从飞禽继承而来
  {
    public boolean CanSpeaking; //申明了麻雀自己的特征.
    public override boolean Fly() {...} ;    //重载飞翔动作,实现自己的飞翔
  }

  class  鹤 : 飞禽  //鹤从飞禽继承而来
  {
    
    public override void Fly() {...} ;    //重载实现鹤的飞翔
  }
  
  //如何来使用虚拟函数,这里同时也是一个多态的例子.
  //打鸟
  void ShootBird(Bird : 飞禽)  //注意这里申明传入一个"飞禽"类,而不是具体的"鸟类". 好处是以后不管出现多少种鸟类,只要是从飞禽继承下来的,都照打不误:)
  {
    if( Bird.fly())
    {
      ....
      开始打鸟...
      ...
    }
  }
  
  static void main()
  {
     //打麻雀
     ShootBird( new 麻雀());
     //打鹤
     ShootBird( new 鹤());     
     //看到没!都是打鸟的过程,我这里可以实现打任何一种鸟了,添加一行代码而不去修改代码
     ShootBird( new 其它的飞禽());          
     ShootBird( ...);          
  }   
--------------------------------------------  
虚拟函数的的执行过程:    
   虚拟函数从C#的程序编译的角度来看,它和其它一般的函数有什么区别呢?一般 函数在编译期间就静态的编译到了执行文件中,在程序运行期间是不发生变化的,也就是写死了!而虚拟函数在编译期间是不被静态编译的,它是不确定的,它会根据运行时期对象实例来动态判断要调用的函数,其中那个申明时定义的类叫申明类,那个执行时实例化的类叫实例类.具体的检查的流程如下:
a.当调用一个对象的函数时,系统会去检查这个对象申明定义的类,即申明类
b.然后它更据这个申明类型的定义去检查这个函数是否虚拟函数
c.如果有virtual关键字,他就认为是虚拟函数,这个时候他又去检查实例类.
d.好!找到这个实例类后,他再检查这个实例类定义中是否重新实现了虚拟函数(通过override),如果是!好了他就不再找了,马上执行它.
e.如果没有,系统又往上层父类找实例类的父类,直到找到一个最近重载了该虚拟函数的父类为止,然后执行该函数.
呵呵,知道这点有什么用呢?搞清楚这个老师的bt题目就可以拿下了.
还是来个简单例子说明问题
class A
{
   protected virtual FuncA() {Console.WriteLine("FuncA")} ;//注意virtual,表明这是一个虚拟函数
}

class B :A             //注意 B是从A类继承,所以A是父类,B是子类
{
   protected override FuncA(){Console.WriteLine("FuncB")} ; //注意override ,表明重新实现了虚拟函数
}

static void main()
{
   A a ;               //定义一个a这个A类对象.这个A就是a的申明类
   A b ;               //定义一个b这个A类的对象.这个A就是b的申明类  
   a = new A() ;      //实例化a对象,A是这次实例类
   a.FuncA() ;        //开始执行FuncA. 1.先照申明类A 2.检查是虚拟方法 3.检查实例类A 4.执行实例类中的方法 5输出结果 FuncA
   b = new B();     //实例化b对象,B是这次实例类
   b.FuncA() ;     //开始执行FuncA. 1.先照申明类A 2.检查是虚拟方法 3.检查实例类B  4.执行实例类中的方法 5输出结果 FuncB
}    

只有注册用户登录后才能发表评论。