(钟豪原创)C#与C++类的继承机制的对比分析(1)

本文对比分析了C#与C++两种语言中类的继承机制,包括继承方式、成员函数隐藏规则及访问基类成员的方法。C#简化了继承方式,仅支持单一继承,并可通过接口实现多重继承。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C#C++类的继承机制的对比分析(1)

1.1 继承的基本概念

为了提高软件模块的可复用性和可扩充性,以便提高软件开发效率,我们总是希望能够通过利用前人或者以前的开发成果,同时又希望在自己的开发过程中能够有足够的灵活性,不拘泥于复用的模块。在面向对象的程序设计语言中是采用一种特殊的机制来实现的,这就是:继承。

继承是面向对象编程的支柱之一,是OOP鼓励代码复用的一种方法。继承让您通过扩展现有基类的功能来创建一个新的派生类。继承也允许建立类的层次结构。公用功能可以放在基类中,可以创建任意数量的派生类来利用此功能,而不用内部复制这些功能的代码。

在建立一个新类时,程序员可以让新类继承预定义基类(base class)的数据成员和成员函数,而不必重新编写新的数据成员和成员函数。这种新类称为派生类(derived class)。在C++中允许派生类从多个基类派生出来,这些基类之间可能毫无关系。但在C# 中只允许单一继承。

1.2 C++C#中关于类继承代码实例对比分析

下面我将给出一个关于交通工具的一个类,以及从交通工具类派生出来的汽车类,通过用C++C#来实现这两个类来对比分析。

C++源代码:

#include<iostream>

using namespace std;

class Vehicle

{

public:

       int wheels;

       float weight;

public:

       void SetData()                           //(A)     

{

          cout<<"Vehicle's SetData() called !"<<endl;

       }

       void SetData(int w,float g)              //(B)

       {

              cout<<"Vehicle's SetData(int,int) called !"<<endl;

       }

};

class Car:public Vehicle    //(C)

{

public:

       int passengers;

       void SetData(int w,float g,int p)    //(D)

       {

          cout<<"Car's SetData(int,int,int) called !"<<endl;

       }

};

 

int main()

{

       Car myCar;

       myCar.Vehicle::SetData();         //(E)

       myCar.Vehicle::SetData(3,4);     //(F)

       myCar.SetData(3,4,5);          //(G)

return 1;

}

C#源代码:

using System;

namespace NameLookup

{

       class Vehicle

       {

              public int wheels;

              public float weight;

              public void SetData()          //(A)

              {

                     Console.WriteLine("Vehicle's SetData() called !");

              }

              public void SetData(int w,int g)         //(B)

              {

                     Console.WriteLine("Vehicle's SetData(int,int) called !");

              }

       }

 

       class Car:Vehicle                 //(C)

       {

              public int passengers;

              public void SetData(int w,float g,int p)   //(D)

              {

                     Console.WriteLine("Car's SetData(int,int,int) called !");

              }

       }

 

       class Class1

       {

              static void Main(string[] args)

              {

                     Car car = new Car();

                     car.SetData();               //(E)

                     car.SetData(2,3);           //(F)

                     car.SetData(2,3,4);        //(G)

              }

       }

}

1.2.1 类继承的方式的对比

首先,我们比较C++源代码中的第C行和C#源代码中的第C行。我们可以看到在C++中的继承是分为: 公有继承,私有继承,保护继承。而在C#中只有默认的公有继承。这样简化了继承的方式,至于限制派生类访问基类的数据以及方法成员,将由基类的数据以及方法成员的访问控制权限决定。

(1)    C#中,派生类只能从一个类中继承。这是因为,在C++中,人们在大多数情况下不需要一个从多个类中派生的类。从多个基类中派生一个类,往往会带来许多问题,从而抵消了这种灵活性带来的优势。C#中的继承符合下列规则:继承是可传递的。如果CB中派生,B又从A中派生,那么C不仅继承了B中声明的成员,同样也继承了A中的成员。Object 类作为所有类的基类。

(2)    派生类应当是对基类的扩展。派生类可以添加新的成员,但不能除去已经继承的成员的定义。构造函数和析构函数不能被继承。除此以外的其它成员,不论对它们定义了怎样的访问方式,都能被继承。基类中成员的访问方式只能决定派生类能否访问它们。

(3)    派生类如果定义了与继承而来的成员同名的新成员,就可以覆盖已继承的成员。但这并不因为这派生类删除了这些成员,只是不能再访问这些成员。类可以定义虚方法、虚属性以及虚索引指示器,它的派生类能够重载这些成员,从而使得类可以展示出多态性。

(4)    C#中,派生类只能从一个类中继承,但可以通过接口实现多重继承。

1.2.2 成员函数的隐藏规则对比

我们可以看到C++C#之间有一个非常巨大的区别:在C++中,派生类中一个给定名称的函数将隐藏基类中的所有的同名函数,不管它们的签名是否相同。在派生类中,我们只能借助作用域分解操作才能访问这些被隐藏的函数。但是在C#中却并非如此,在C#中会根据函数签名而去调用基类中的同名但不同签名的函数。而在C++中,虽然基类Vehicle中的成员函数SetData与派生类Car中的成员函数SetData具有不同的函数签名:例如C++源程序中,第AB行代码处的void SetData()void SetData(int w,float g)函数将被派生类的void SetData(int w,float g,int p)函数覆盖掉,因此如果我们把C++源程序中的第E,F行分别改为 myCar.SetData();   myCar.SetData(3,4)。程序是无法通过编译的。

1.2.3 访问与隐藏基类成员对比分析

u     C++中访问基类成员的方法

示例:下面程序中基类 Person 和派生类 Employee 都有一个名为 Getinfo 的方法。通过使用类作用域分辨符,可以从派生类中调用基类上的 Getinfo 方法。

#include<iostream>

#include<string>

using namespace std;

class Person                                                                                        

{

protected :

       string ssn;

       string name;

public:

       Person()

       {

              ssn = "111-222-333-444";

              name = "Howells";

       };

    virtual void GetInfo()

       {

              cout<<"姓名: "<<name<<endl;

              cout<<"编号: "<<ssn<<endl;

       }

};

class Employee:public Person

{

public:

       string id ;

public:

       Employee()

       {

              id = "1999";

       }

       virtual void GetInfo()

       {

              Person::GetInfo();     //A

         cout<<"成员ID: "<<id<<endl;

       }

};

 

void main()

{

       Employee howells;

       howells.GetInfo();

}

 

程序运行结果:

姓名: Howells

编号: 111-222-333-444

成员ID: 1999

Press any key to continue

 

可见在C++中,派生类是通过类作用域来访问基类中的方法的。在以上程序中派生类Employee通过使用基类名加范围分解符的方法,如:第A行代码Person::GetInfo()

u     C#中访问基类成员的方法

示例:下面程序中基类 Person 和派生类 Employee 都有一个名为 Getinfo 的方法。通过使用 base 关键字,可以从派生类中调用基类上的 Getinfo 方法。

using System;

namespace fanwenjilei

{

       public class Person

       {

              protected string ssn = "111-222-333-444" ;

              protected string name = "Howells" ;

              public virtual void GetInfo()

              {

                     Console.WriteLine("姓名: {0}", name) ;

                     Console.WriteLine("编号: {0}", ssn) ;

              }

       }

       class Employee: Person

       {

              public string id = "1999" ;

              public override void GetInfo()

              {

                     // 调用基类的GetInfo方法:

                     base.GetInfo();

                     Console.WriteLine("成员ID: {0}", id) ;

              }

       }

       class TestClass

       {

              public static void Main()

              {

                     Employee E = new Employee() ;

                     E.GetInfo() ;

              }

       }

}

程序运行结果:

姓名: Howells

编号: 111-222-333-444

成员ID: 1999

 

C#中可以通过base关键字来访问基类成员,具体的访问规则如下:

(1)    调用基类上已被其他方法重写的方法。

(2)    指定创建派生类实例时应调用的基类构造函数。

(3)    基类访问只能在构造函数、实例方法或实例属性访问器中进行。

(4)    从静态方法中使用 base 关键字是错误的。

u     C#中阻止类被继承的方法

如果所有的类都可以被继承,继承的滥用会带来什么后果?类的层次结构体系将变得十分庞,大类之间的关系杂乱无章,对类的理解和使用都会变得十分困难。有时候,我们并不希望自己编写的类被继承。另一些时候,有的类已经没有再被继承的必要。C#提出了一个密封类(sealed class)的概念,帮助开发人员来解决这一问题。

密封类在声明中使用sealed 修饰符,这样就可以防止该类被其它类继承。如果试图将一个密封类作为其它类的基类,C#将提示出错。理所当然,密封类不能同时又是抽象类,因为抽象总是希望被继承的。

在哪些场合下使用密封类呢?密封类可以阻止其它程序员在无意中继承该类。而且密封类可以起到运行时优化的效果。实际上,密封类中不可能有派生类。如果密封类实例中存在虚成员函数,该成员函数可以转化为非虚的,函数修饰符virtual 不再生效。让我们看下面的例子:

abstract class A
{
   public abstract void F( ) ;
}
sealed class B: A
{
   public override void F( ){ // F
的具体实现代码 }
}

如果我们尝试写下面的代码 class C: B{ }C#会指出这个错误,告诉你B 是一个密封类,不能试图从B 中派生任何类。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值