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

14 篇文章 0 订阅
11 篇文章 0 订阅
       面向对象设计,顾名思义,是以对象为核心。分析出现实世界中对象,这些对象含有状态和行为,其中,状态对应着属性,行为对应着方法。除了静态分析除了对象之外,还要研究这些对象之间的动态关系。
       在程序设计中,为了实现上诉的分析,面向对象语言常通过封装,继承,多态等特性来实现面向对象设计的。其中,封装理解起来很简单,有两层意思,一个是把现实世界对象的状态和特性封装起来;另外,对象只允许外部类调用一部分的属性和方法,而保留一些私有的特性供内部使用,向调用客户代码屏蔽对象模型中的一些私有属性;继承,这是面向对象语言最常提到的概念,目的是实现代码的复用;多态,这个特性是用同样的一个接口,来调用不同的对象的方法。多态主要是抽象的表现,向调用客户代码屏蔽复杂的对象模型,使程序更具有扩展性。
       那么,我们在学习一种新的面向对象语言的时候,通常,首先包括语言的基本变量,逻辑控制分支,函数定义,其次主要的就为揭示语言是如何支持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++没有很好的区分接口类和抽象类的后果。
class IDicomFunction//当作接口
{
public:
	virtual void Exp2File() = 0;
	virtual void Exp2Disk() = 0;
	virtual void Exp2Film() = 0;
};

void IDicomFunction::Exp2Disk()//表示接口是必须被重写的(有协议意思),而且还提供了缺省实现(有复用的意思):接口和缺省实现实现分离
{
	cout<<"IDicomFunction Exp2File is called"<<endl;
}

class CPerson
{
protected://可以修改访问说明符
	virtual void Say()
	{
		cout<<"CPerson Say is called"<<endl;
	}
};

class CPatient: public CPerson, public IDicomFunction
{
public :
	void Say()
	{
		cout<<"patient say CPatient called"<<endl;
		CPerson::Say();
	}
	void Exp2File()//无论是否写virtual关键字,此函数是虚函数的属性都不会更改
	{
		IDicomFunction::Exp2Disk();//
		cout<<"exp 2 file CPatient is called"<<endl;
	}
	virtual void Exp2Disk()
	{
		cout<<"exp 2 disk CPatient is called"<<endl;
	}
	virtual void Exp2Film()
	{
		cout<<"exp 2 film CPatient is called"<<endl;
	}
};

class CChinaPatient:public CPatient
{
private:
	void Exp2File()//改变CChinaPatient的访问权限
	{
		cout<<"chinaPatient is called"<<endl;
		CPatient::Exp2File();
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	CPatient* pPatient = new CChinaPatient;
	pPatient->Exp2File();//可以调用CChinaPatient中的私有函数
	system("pause");
	return 0;
}


        C#中,对接口和抽象在形式上区分了开来,接口类是通过关键字interface来修饰,抽象类是通过abstract关键字修饰(包含有抽象函数的类,即为抽象类)。 C#中规定,子类只能继承于一个父类,也就是一个子类只能is a一个父类,但可以遵循多个接口(协议) 。类中函数用virtual修饰就是虚函数,必须提供函数主体,供子类进行覆盖或继承。
       用abstract来修饰即为抽象函数(不能提供函数体实现,假设可以提供函数体实现,也是没有意义的。因为,抽象类不能实例化,且抽象函数必须被覆盖(override),那么,当调用抽象函数时,就必定是子类的覆盖后的函数体实现)#1,子类必须进行(override)覆盖#2。
       其中,在C#中,abstract或者virtual函数不能是private访问类型#3,而且在覆盖父类虚函数或者抽象函数时,不能修改覆盖函数的访问修饰符#4。
       接口类的含义是一组协议,需要实体类进行遵守。其所有的函数都是共有类型,在函数申明前不能加任何访问修饰符#5。
       在C#继承语法中,虚函数和函数其实是一类函数,是可以相互转化的。即,虚函数可以被子类隐藏为一个一般函数,使函数失去多态特性;同时,一般函数也可以被子类隐藏为虚函数,而具有了多态特性;同时,隐藏函数时,访问权限是可以被修改的#6。 也就是说,要想实现多态,必须要添加override关键字 ,在override的修饰下,访问权限是绝不能被修改的;如果没有override进行修饰,那么,可以认定子类是在隐藏父类的函数,此时函数没有多态特性,这里要注意一下,如果没有override的修饰,最好要用new来显式来进行隐藏,否则,编译器会给出警告。此特性和C++相悖,C++中,一朝被定义为virtual函数,一直都是虚函数,无论在子类中,是否加入virtual关键字,都是具有多态特性的。
   
    interface ExpDcm
    {
        //public void ExpDcmFuction ();//#5错误
        void ExpDcmFuction ();
        void ExpDcm2Pacs ();
        void ExpDcm2Film ();
        void ExpDcm2MediaDisk ();
    }
   public abstract class Person
    {
        private string name ;
        public string Name
        {
            get
            {
                return name ;
            }
            set
            {
                name = value ;
            }
        }
          protected abstract void Bing ();
        public abstract void Call();//#1不能提供函数体
        //private abstract void Bing ();//#3编译错误,抽象函数不能被私有访问符修饰
    }
  public class Patient : Person, ExpDcm
    {
          //protected override void Call ()//#4不能修改继承而来函数体的访问级别
        public override void Call()//#2实现抽象函数
        {
            Console.WriteLine (@"call is called");
        }
        public void ExpDcmFuction()//实现接口
        {
            Console.WriteLine(@"patient exp dcm function is call" );
        }
        public virtual void ExpDcm2Pacs() //实现接口,并且增加虚函数属性
        {
            Console.WriteLine(@"patient exports dcm file and send to pacs" );
        }
        public virtual void ExpDcm2Film() //实现接口,并且增加虚函数属性
        {
            Console.WriteLine(@"patient exports dcm file and send to film" );
        }
        void ExpDcm .ExpDcm2MediaDisk()//只能通过接口来访问,不能添加public 属性
        {
            Console.WriteLine(@"patient exports dcm file and send to disk" );
        }
         public void GetId()
        {
            Console.WriteLine (@"the patient GetId is call");
        }
    }
    public class ChinaPatient :Patient
    {
         new protected virtual void GetId ()//#6虚函数覆盖一般函数,使函数具有多态特性;并且修改访问类型
        {
            Console .WriteLine ( @"china patient GetId called" );
        }
        public override void  Call()//实现抽象函数
        {
            Console.WriteLine (@"china patient is called");
        }
        new protected void ExpDcmFuction()//隐藏一般函数,可修改隐藏函数的访问类型
        {
            Console.WriteLine(@"ch patient exp dcm is call");
        }
        new public virtual void ExpDcm2Pacs() //隐藏父类虚函数
        {
            Console.WriteLine(@"china patient exports dcm file and send to pacs");
        }
        public override void ExpDcm2Film()//重写父类虚函数
        {
            Console.WriteLine(@"china exports dcm file and send to film");
        }
    }

还有partical的用法,也可以复用代码;

         ObjC中,没有虚函数之说,因为在ObjC的世界里,所有的函数都是动态调用的,可参考我之前写的一篇关于ObjC函数调用的文章;而C++的函数有的动态调用,有的是静态调用的,动态调用是通过虚指针和虚表来实现的,算不上优美。
         同C#一样,ObjC中,只能继承于一个类,但同时可以遵循实现多个protocol协议(接口),在ObjC中,成员函数都是public的,所以,也不同区分访问权限的问题,简单实用。相关的一些代码可以参考以下
#import <Foundation/Foundation.h>

/**实现关于Dicom的相关接口协议
 */
@protocol DcmFunction
@required
-(void) ExpDcm2Pacs;
-(void) ExpDcm2Film;
@optional
-(void) ExpDcm2Disk;
@end

/**person接口
 */
@interface Person: NSObject
{
    NSString* strName;
    int nAge;
    int nSex;//缺省是protected ; 子类中可以进行访问
}
@property(nonatomic, assign) int nAge;//定义属性的一种方式,和字段是相同的名字
@end

/**Patient
 */
@interface Patient:Person<DcmFunction>
{
    NSString* strAccessNumber;
}
@property(nonatomic, copy) NSString* access_number;//定义属性的方法,在synthesize中,把字段赋值给属性
-(void) Say;
@end
/**.m文件
*/
@implementation Person
@synthesize nAge;
-(void) Say
{
    NSLog(@"Person's Say called");
}
@end

@interface Patient(PrivateMethod)
-(void) GetInstanceUid;
@end

@implementation Patient//给出警告:Method <> in protocol not implemented
@synthesize access_number = strAccessNumber;//属性的定义方式

-(void) GetInstanceUid//定义私有方法,利用的是类别(categary),在外部可以向此类发送此消息,那么,此类的实例将进行响应
{
    NSLog(@"patient get instance uid");
}
-(void) Say
{
    NSLog(@"Patient's Say called");
}
//实现接口
-(void) ExpDcm2Pacs
{
    NSLog(@"expose to pacs");
}
-(void) ExpDcm2Film
{
    NSLog(@"expose to film");
}
@end
/**main
*/
    Person* pPatient = [[Patient alloc]init];
    if([pPatient respondsToSelector:@selector(GetInstanceUid)] == YES)
    {
        [pPatient performSelector:@selector(GetInstanceUid)];
    }
    [pPatient release];


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值