返回首页

谈面向对象编程OOP——C/C++,C#,Objective C .

时间:2012-11-06 10:33来源:csdn 作者:rendawei636 点击:
面向对象设计,顾名思义,是以对象为核心。分析出现实世界中对象,这些对象含有状态和行为,其中,状态对应着属性,行为对应着方法。除了静态分析除了对象之外,还要研究这些
  

     面向对象设计,顾名思义,是以对象为核心。分析出现实世界中对象,这些对象含有状态和行为,其中,状态对应着属性,行为对应着方法。除了静态分析除了对象之外,还要研究这些对象之间的动态关系。

 
 
     在程序设计中,为了实现上诉的分析,面向对象语言常通过封装,继承,多态等特性来实现面向对象设计的。其中,封装理解起来很简单,有两层意思,一个是把现实世界对象的状态和特性封装起来;另外,对象只允许外部类调用一部分的属性和方法,而保留一些私有的特性供内部使用,向调用客户代码屏蔽对象模型中的一些私有属性;继承,这是面向对象语言最常提到的概念,目的是实现代码的复用;多态,这个特性是用同样的一个接口,来调用不同的对象的方法。多态主要是抽象的表现,向调用客户代码屏蔽复杂的对象模型,使程序更具有扩展性。
 
 
       那么,我们在学习一种新的面向对象语言的时候,通常,首先包括语言的基本变量,逻辑控制分支,函数定义,其次主要的就为揭示语言是如何支持OOP特性的语法,也就是如何支持封装,继承,多态的语法。
 
 
                   
1.封装:访问权限问题    
          C++体系中,字段和方法都用private,protected, public三个不同访问权限级别的关键字来修饰,缺省是private类型;其中,属性和字段没有用关键字加以区分,只能,程序员自己定义方法,来对字段进行灵活权限的访问。
 
 
       C#中,字段,属性和方法,同样可以通过private,protected,public修饰来控制封装权限,private只能在本类中调用,protected可以在子类中调用,public在外部和内部都可以进行访问。除此之外,还增加了internal的关键字来控制成员在模块间的访问级别,但要注意internal只能和其他三个访问关键词联合使用。
      字段还可以由readonly关键字来修饰,表示字段只能由构造函数或者初始化赋值语句进行赋值。
      属性可以用set,get来控制外部的访问,他们也同样可以用private,protected,public来修饰,但受控于属性的整体访问权限。
 
 
      ObjC中,同样有private protected(可以由其子类进行访问) public的区别,对于字段,缺省属性和C++不同,是protected属性。同样,字段可以申明为@private,@protected,@public的形式,外部访问时@public字段时,可以用(对象->字段)的形式来进行访问。
      通常我们都会保留ObjC中字段的缺省访问权限,用属性来对字段进行包装来控制访问权限,objC中有定义属性的关字@property,@synthesize,属性可以用[readwrite(缺省)/ readonly]; [assign/ retain(缺省)/ copy]; [atomicity/ nonatomic]来进行修饰。在ObjC中,属性的实现方式是由编译器自动添加[set<字段>]和[<字段>]的方法来对字段进行访问的,可以用<.>来对属性进行访问,这里一定要注意,属性和字段的区别,属性和对象的retainCount有很大的关系,也就是和内存管理有关系,而字段完全没有此特点。
      对象方法的访问权限都是public的,如果需要私有方法,就不要在.h文件中申明,只许在 .m文件中直接进行定义就可以。但ObjC的方法调用,都是通过发送消息来实现的,如果外部客户知道私有方法的函数签名,那么,就可以对此方法进行调用,这一点没有C++和C#语法严谨。再多说一点,申明私有方法的做法在ObjC中有一个很响亮的名字——类别(categary)。是一种可以扩展原有类功能的语法。用此语法可以在编写一个类时,分组(人员,文件)实现,也可以实现非正式委托 (informal protocol)。在C#中,也有同样的用法,C++中没有此特点。
       
 
2.继承:复用问题;多态:动态绑定
      继承是面向对象编程语言中最重要的应用。这篇文章中,我只谈一下,其中的接口和抽象在三中语言中的用法。关于子类如何继承父类中的字段和方法,读者可以搜索其他文件进行了解。
 
      在C++中,接口和抽象类在形式上没有明显的区别。可以直观的认为包含有纯虚函数(virtual <函数签名>=0;)的类都是接口类或者抽象类,实际编程中,在意义上是有区别的。纯虚函数在子类中,必须提供实现,才能实例化,纯虚函数可以提供缺省的实现,见下文例子纯虚函数可以是private, protected, public三种访问类型,子类中,可以修改这些访问类型。
      在C++中,可以多重继承,这是其他两种语言中所没有的特性,同时,也引入了钻石结构的缺点,引入了virtual虚继承。这也是C++没有很好的区分接口类和抽象类的后果。
[cpp] view plaincopyprint?
  1. class IDicomFunction//当作接口   
  2. {  
  3. public:  
  4.     virtual void Exp2File() = 0;  
  5.     virtual void Exp2Disk() = 0;  
  6.     virtual void Exp2Film() = 0;  
  7. };  
  8.   
  9. void IDicomFunction::Exp2Disk()//表示接口是必须被重写的(有协议意思),而且还提供了缺省实现(有复用的意思):接口和缺省实现实现分离   
  10. {  
  11.     cout<<"IDicomFunction Exp2File is called"<<endl;  
  12. }  
  13.   
  14. class CPerson  
  15. {  
  16. protected://可以修改访问说明符   
  17.     virtual void Say()  
  18.     {  
  19.         cout<<"CPerson Say is called"<<endl;  
  20.     }  
  21. };  
  22.   
  23. class CPatient: public CPerson, public IDicomFunction  
  24. {  
  25. public :  
  26.     void Say()  
  27.     {  
  28.         cout<<"patient say CPatient called"<<endl;  
  29.         CPerson::Say();  
  30.     }  
  31.     void Exp2File()//无论是否写virtual关键字,此函数是虚函数的属性都不会更改   
  32.     {  
  33.         IDicomFunction::Exp2Disk();//   
  34.         cout<<"exp 2 file CPatient is called"<<endl;  
  35.     }  
  36.     virtual void Exp2Disk()  
  37.     {  
  38.         cout<<"exp 2 disk CPatient is called"<<endl;  
  39.     }  
  40.     virtual void Exp2Film()  
  41.     {  
  42.         cout<<"exp 2 film CPatient is called"<<endl;  
  43.     }  
  44. };  
  45.   
  46. class CChinaPatient:public CPatient  
  47. {  
  48. private:  
  49.     void Exp2File()//改变CChinaPatient的访问权限   
  50.     {  
  51.         cout<<"chinaPatient is called"<<endl;  
  52.         CPatient::Exp2File();  
  53.     }  
  54. };  
  55.   
  56. int _tmain(int argc, _TCHAR* argv[])  
  57. {  
  58.     CPatient* pPatient = new CChinaPatient;  
  59.     pPatient->Exp2File();//可以调用CChinaPatient中的私有函数   
  60.     system("pause");  
  61.     return 0;  
  62. }  

 
 
 
      C#中,对接口和抽象在形式上区分了开来,接口类是通过关键字interface来修饰,抽象类是通过abstract关键字修饰(包含有抽象函数的类,即为抽象类)。C#中规定,子类只能继承于一个父类,也就是一个子类只能is a一个父类,但可以遵循多个接口(协议)。类中函数用virtual修饰就是虚函数,必须提供函数主体,供子类进行覆盖或继承。
      用abstract来修饰即为抽象函数(不能提供函数体实现,假设可以提供函数体实现,也是没有意义的。因为,抽象类不能实例化,且抽象函数必须被覆盖(override),那么,当调用抽象函数时,就必定是子类的覆盖后的函数体实现)#1,子类必须进行(override)覆盖#2。
      其中,在C#中,abstract或者virtual函数不能是private访问类型#3,而且在覆盖父类虚函数或者抽象函数时,不能修改覆盖函数的访问修饰符#4。
      接口类的含义是一组协议,需要实体类进行遵守。其所有的函数都是共有类型,在函数申明前不能加任何访问修饰符#5。
      在C#继承语法中,虚函数和函数其实是一类函数,是可以相互转化的。即,虚函数可以被子类隐藏为一个一般函数,使函数失去多态特性;同时,一般函数也可以被子类隐藏为虚函数,而具有了多态特性;同时,隐藏函数时,访问权限是可以被修改的#6。也就是说,要想实现多态,必须要添加override关键字,在override的修饰下,访问权限是绝不能被修改的。此特性和C++相悖,C++中,一朝被定义为virtual函数,一直都是虚函数,无论在子类中,是否加入virtual关键字,都是具有多态特性的。
   
[csharp] view plaincopyprint?
  1.   interface ExpDcm  
  2.   {  
  3.       //public void ExpDcmFuction ();//#5错误   
  4.       void ExpDcmFuction ();  
  5.       void ExpDcm2Pacs ();  
  6.       void ExpDcm2Film ();  
  7.       void ExpDcm2MediaDisk ();  
  8.   }  
  9.  public abstract class Person  
  10.   {  
  11.       private string name ;  
  12.       public string Name  
  13.       {  
  14.           get  
  15.           {  
  16.               return name ;  
  17.           }  
  18.           set  
  19.           {  
  20.               name = value ;  
  21.           }  
  22.       }  
  23.         protected abstract void Bing ();  
  24.       public abstract void Call();//#1不能提供函数体   
  25.       //private abstract void Bing ();//#3编译错误,抽象函数不能被私有访问符修饰   
  26.   }  
  27. public class Patient : Person, ExpDcm  
  28.   {  
  29.         //protected override void Call ()//#4不能修改继承而来函数体的访问级别   
  30.       public override void Call()//#2实现抽象函数   
  31.       {  
  32.           Console.WriteLine (@"call is called");  
  33.       }  
  34.       public void ExpDcmFuction()//实现接口   
  35.       {  
  36.           Console.WriteLine(@"patient exp dcm function is call" );  
  37.       }  
  38.       public virtual void ExpDcm2Pacs() //实现接口,并且增加虚函数属性   
  39.       {  
  40.           Console.WriteLine(@"patient exports dcm file and send to pacs" );  
  41.       }  
  42.       public virtual void ExpDcm2Film() //实现接口,并且增加虚函数属性   
  43.       {  
  44.           Console.WriteLine(@"patient exports dcm file and send to film" );  
  45.       }  
  46.       void ExpDcm .ExpDcm2MediaDisk()//只能通过接口来访问,不能添加public 属性   
  47.       {  
  48.           Console.WriteLine(@"patient exports dcm file and send to disk" );  
  49.       }  
  50.        public void GetId()  
  51.       {  
  52.           Console.WriteLine (@"the patient GetId is call");  
  53.       }  
  54.   }  
  55.   public class ChinaPatient :Patient  
  56.   {  
  57.        new protected virtual void GetId ()//#6虚函数覆盖一般函数,使函数具有多态特性;并且修改访问类型   
  58.       {  
  59.           Console .WriteLine ( @"china patient GetId called" );  
  60.       }  
  61.       public override void  Call()//实现抽象函数   
  62.       {  
  63.           Console.WriteLine (@"china patient is called");  
  64.       }  
  65.       new protected void ExpDcmFuction()//隐藏一般函数,可修改隐藏函数的访问类型   
  66.       {  
  67.           Console.WriteLine(@"ch patient exp dcm is call");  
  68.       }  
  69.       new public virtual void ExpDcm2Pacs() //隐藏父类虚函数   
  70.       {  
  71.           Console.WriteLine(@"china patient exports dcm file and send to pacs");  
  72.       }  
  73.       public override void ExpDcm2Film()//重写父类虚函数   
  74.       {  
  75.           Console.WriteLine(@"china exports dcm file and send to film");  
  76.       }  
  77.   }  
还有partical的用法,也可以复用代码;
 
       ObjC中,没有虚函数之说,因为在ObjC的世界里,所有的函数都是动态调用的,可参考我之前写的一篇关于ObjC函数调用的文章;而C++的函数有的动态调用,有的是静态调用的,动态调用是通过虚指针和虚表来实现的,算不上优美。
       同C#一样,ObjC中,只能继承于一个类,但同时可以遵循实现多个protocol协议(接口),在ObjC中,成员函数都是public的,所以,也不同区分访问权限的问题,简单实用。相关的一些代码可以参考以下
[cpp] view plaincopyprint?
  1. #import <Foundation/Foundation.h>   
  2.   
  3. /**实现关于Dicom的相关接口协议
  4.  */  
  5. @protocol DcmFunction  
  6. @required  
  7. -(void) ExpDcm2Pacs;  
  8. -(void) ExpDcm2Film;  
  9. @optional  
  10. -(void) ExpDcm2Disk;  
  11. @end  
  12.   
  13. /**person接口
  14.  */  
  15. @interface Person: NSObject  
  16. {  
  17.     NSString* strName;  
  18.     int nAge;  
  19.     int nSex;//缺省是protected ; 子类中可以进行访问   
  20. }  
  21. @property(nonatomic, assign) int nAge;//定义属性的一种方式,和字段是相同的名字   
  22. @end  
  23.   
  24. /**Patient
  25.  */  
  26. @interface Patient:Person<DcmFunction>  
  27. {  
  28.     NSString* strAccessNumber;  
  29. }  
  30. @property(nonatomic, copy) NSString* access_number;//定义属性的方法,在synthesize中,把字段赋值给属性   
  31. -(void) Say;  
  32. @end  
  33. /**.m文件
  34. */  
  35. @implementation Person  
  36. @synthesize nAge;  
  37. -(void) Say  
  38. {  
  39.     NSLog(@"Person's Say called");  
  40. }  
  41. @end  
  42.   
  43. @interface Patient(PrivateMethod)  
  44. -(void) GetInstanceUid;  
  45. @end  
  46.   
  47. @implementation Patient//给出警告:Method <> in protocol not implemented   
  48. @synthesize access_number = strAccessNumber;//属性的定义方式   
  49.   
  50. -(void) GetInstanceUid//定义私有方法,利用的是类别(categary),在外部可以向此类发送此消息,那么,此类的实例将进行响应   
  51. {  
  52.     NSLog(@"patient get instance uid");  
  53. }  
  54. -(void) Say  
  55. {  
  56.     NSLog(@"Patient's Say called");  
  57. }  
  58. //实现接口   
  59. -(void) ExpDcm2Pacs  
  60. {  
  61.     NSLog(@"expose to pacs");  
  62. }  
  63. -(void) ExpDcm2Film  
  64. {  
  65.     NSLog(@"expose to film");  
  66. }  
  67. @end  
  68. /**main
  69. */  
  70.     Person* pPatient = [[Patient alloc]init];  
  71.     if([pPatient respondsToSelector:@selector(GetInstanceUid)] == YES)  
  72.     {  
  73.         [pPatient performSelector:@selector(GetInstanceUid)];  
  74.     }  
  75.     [pPatient release];  


 
------分隔线----------------------------

  • 李老师
  • 李老师
  • 胡老师
  • 胡老师
合作伙伴
  • 武汉工程大学合作培训机构

  • 国家信息技术紧缺人才培养工程(NITE)

  • ARM公司全球授权培训中心

  • 国内首家Symbian授权培训

  • 微软全球嵌入式合作伙伴

  • Altera全球合作培训机构

在线客服
  • 客服热线:
    139-8620-3604
    159-2752-9536



  • 官方微信
  •  咨询微信二维码