为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

C#与C++异同

2013-08-20 28页 pdf 454KB 30阅读

用户头像

is_343334

暂无简介

举报
C#与C++异同 C#与 C++异同 1 细微差异 ............................................................................................................................................................... 1 1.1 类的声明 .............................................................................
C#与C++异同
C#与 C++异同 1 细微差异 ............................................................................................................................................................... 1 1.1 类的声明 .................................................................................................................................................... 1 1.2 同名覆盖 .................................................................................................................................................... 1 2 多重继承 ............................................................................................................................................................... 1 3 C#虚方法和 C++中的虚函数 ................................................................................................................................. 3 4 数组 ....................................................................................................................................................................... 7 5 C#中按值传递和按引用传递 ................................................................................................................................. 7 6 字符串 ................................................................................................................................................................... 9 7 抽象类 ................................................................................................................................................................... 9 7.1 抽象类的构造函数................................................................................................................................... 11 8 C#类访问控制修饰符........................................................................................................................................... 13 9 C#中的类型转换 .................................................................................................................................................. 13 10 C#委托(delegate) ................................................................................................................................................ 14 11 C#中事件(event) ................................................................................................................................................. 17 12 接口(interface) ................................................................................................................................................... 19 12.1 接口的使用 ............................................................................................................................................ 19 12.2 接口的多继承 ........................................................................................................................................ 22 12.3 接口的重新实现..................................................................................................................................... 24 12.4 接口和多态 ............................................................................................................................................ 25 12.5 接口的应用举例..................................................................................................................................... 26 12.6 接口与抽象类的比较 ............................................................................................................................. 27 13 密封 ................................................................................................................................................................... 28 13.1 密封类 .................................................................................................................................................... 28 13.2 密封方法 ................................................................................................................................................ 28 1 细微差异 1.1 类的声明 C++的类声明结束后要在最后的大括号后面用分号结尾。C#则可用可不用,往往都是省略。 1.2 同名覆盖 C++中,如果基类和派生类有相同的成员,遵守同名覆盖原则,即派生类对象访问的是派生类的同名 成员,基类对象访问的是基类同名成员。 2 多重继承 C++可以多重继承,即一个派生类可以从多个基类同时继承,而 C#只能从一个类单重继承,但可以 通过接口实现多重继承。C++中如果基类和派生类中有相同成员时,遵循同名覆盖原则,即派生类对象使 用的是派生类同名成员,如果要在派生类中使用基类被覆盖的同名成员(称为派生类和基类互相通信)或 派生类外通过派生类对象访问基类同名成员,都必须使用基类名限定,如 A::f(),举例如下: Class A { Public: void f(){}; }; Class B:A { Public: void f() { A::f();//在派生类中访问基类成员 f(),互相通信 } }; 1 Void main() { A a; A *pa;//定义一个基类型指针 B b; pa=&b;//赋值兼容原则,基类指针可以指向派生类 a.f();//基类对象访问基类成员 f() b.f();//派生类对象访问类 B的 f() pa->f();//访问的是基类 A的 f() b.A::f();//派生类对象访问基类 A的 f() } 而 C#中在派生类访问基类被覆盖的同名成员时使用 base关键字来限定。C#还可以通过显式类型转 换来访问基类成员,如:((Parent)child).print(),其中 child 是派生类的对象,Parent 是基类名, 那么这个语句就是通过派生类对象访问基类成员。举例如下: using System; public class B0 { public void fb() //基类同名方法 { Console.WriteLine("B0's fb called!"); } } public class B1:B0 { public new void fb()//派生类同名方法,new示有意覆盖,若无会产生编译警告 { base.fb();//相互通信,派生类中访问基类方法 Console.WriteLine("B1's fb called!"); } } public class C { public static void Main() { B0 b0 = new B0(); b0.fb();//访问基类方法 B0 b1 = new B1();//基类型的引用指向派生类 b1.fb();//访问基类方法 B1 b2 = new B1(); b2.fb();//访问派生类方法 ((B0)b2).fb();//显式类型转换访问基类方法 Console.ReadLine(); } } 多态性就是指在程序运行时,执行的虽然是一个调用方法的语句,却可以根据派生类对象的类型不同 完成方法的不同的具体实现。在使用虚函数实现多态时,C++派生类覆盖基类虚函数时函数名前可写 virtual,也可以不写,默认为虚函数,在 C++中,由于基类和派生类的赋值兼容原则,可以定义一个基 类指针或引用指向派生类对象,那么通过该指针或引用访问的还是基类同名成员,除非把基类同名成员定 义成虚函数,那么基类指针或引用指向派生类对象访问的是派生类同名成员。上例代码修改如下: Class A { Public: Virtual void f(){};//f()定义成虚函数 }; Class B:A { Public: Virtual void f()//派生类 virtual关键字可省略,默认为虚函数 { 2 A::f();//在派生类中访问基类成员 f(),互相通信 } }; Void main() { A a; a.f();//基类对象访问基类成员 f() B b; b.f();//派生类对象访问类 B的 f() b.A::f();//派生类对象访问基类 A的 f() A *pa;//定义一个基类型指针 pa=&b;//赋值兼容原则,基类指针可以指向派生类 pa->f();//访问的是派生类 B的 f(),虚函数的作用 } C#派生类覆盖基类虚函数必须把基类虚函数 virtual 处写成 override,如:基类中虚函数 public virtual void f()那么派生类写成 public override void f()。上例修改如下: using System; public class B0//public可省略,默认为public访问修饰符 { public virtual void fb() //基类虚方法 { Console.WriteLine("B0's fb called!"); } } public class B1:B0 { public override void fb()//派生类覆盖虚方法 { base.fb();//相互通信,派生类中访问基类方法 Console.WriteLine("B1's fb called!"); } } public class C { public static void Main() { B0 b0 = new B0();//基类对象 b0.fb();//访问基类方法 B0 b1 = new B1();//基类型的引用指向派生类对象 b1.fb();//访问派生了类方法 B1 b2 = new B1();//派生类对象 b2.fb();//访问派生类方法 ((B0)b2).fb();//显式类型转换访问派生类方法 Console.ReadLine(); } } 3 C#虚方法和 C++中的虚函数 首先来观察 C++中的一段程序,如下: #include "iostream.h" class B0 { public: void f() {cout<<"B0.f"<f(); } void main() { B0 b0;//基类对象 B1 b1; B2 b2; b0=b1;//把派生类对象赋给基类对象,赋值兼容原则 b0.f();//输出 B0.f b1.f();//同名覆盖原则,输出 B1.f b2.f();//同名覆盖原则,输出 B2.f B0 *p;//基类型指针 p=&b1;//基类指针指向派生类,赋值兼容原则 fun(p); //因为是基类指针,所以输出 B0.f p=&b2; p->f();//因为是基类指针,所以输出 B0.f } 如果把上例中基类 B0的 f()函数写成虚函数,那么同样的main()运行结果不同,构成多态,修改如下: class B0 { public: virtual void f() {cout<<"B0.f"<f();//因为虚函数构成多态,所以输出 B2.f } 我们再来观察一段 C#程序,如下: class B0 { public void f() { Console.WriteLine("B0.f"); } } class B1:B0 { public void f() { 4 Console.WriteLine("B1.f"); } } class B2 : B1 { public void f() { Console.WriteLine("B2.f"); } } class Program { static void Main(string[] args) { B0 b0 = new B0();//基类对象 b0.f();//输出B0.f B0 b1 = new B1();//隐式类型转换 b1.f();//输出B0.f,和C++中类似。 B2 b2 = new B2();//派生类对象 b2.f();//输出B2.f Console.ReadLine(); } } 如果也把基类中的 f()函数写成虚方法,那么子类必须用 override覆盖 虚方法,构成多态,修改程序如下: class B0 { public virtual void f() { Console.WriteLine("B0.f"); } } class B1:B0 { public override void f() { Console.WriteLine("B1.f"); } } class B2 : B1 { public override void f() { Console.WriteLine("B2.f"); } } class Program { static void Main(string[] args) { B0 b0 = new B0();//基类对象 b0.f();//输出B0.f B0 b1 = new B1();//隐式类型转换 b1.f();//输出B1.f,由于是虚方法,和C++中类似。 B2 b2 = new B2();//派生类对象 b2.f();//输出B2.f Console.ReadLine(); } } 如果基类方法写成虚方法,可选择使用 override重写该方法,也可以选择使用 new不重写方法。 再写一个程序演示多态,如下: 5 class Employee { protected string name; public Employee() {} public Employee(string _name) { name = _name; } public virtual void StartWork() { Console.Write(name+"开始工作:"); } } class Manager:Employee { public Manager(string name):base(name) {} public override void StartWork() { base.StartWork(); Console.WriteLine("分配工作"); } } class Saler:Employee { public Saler(string name):base(name) {} public override void StartWork() { base.StartWork(); Console.WriteLine("推销产品"); } } class Technolgy : Employee { public Technolgy(string name) : base(name) { } public override void StartWork() { base.StartWork(); Console.WriteLine("技术开发"); } } class Program { static void Main(string[] args) { Employee[] emp=new Employee[3]; emp[0] = new Manager("张三"); emp[1] = new Saler("李四"); emp[2] = new Technolgy("王五"); foreach(Employee e in emp) { e.StartWork(); } Console.ReadLine(); } } 运行结果: 张三开始工作:分配工作 李四开始工作:推销产品 王五开始工作:技术开发 6 4 数组 在 C#中,数组实际上是对象。System.Array 是所有数组类型的抽象基类型。提供创建、操作、搜 索和排序数组的方法,因而在公共语言运行库中用作所有数组的基类。声明数组: int[] a=new int[5]; 可以同时初始化: int[] a=new int[5] {1,2,3,4,5}; 或 int[] a={1,2,3,4,5}; 举例如下: int[] a = new int[10] { 12,4,5,23,-5,21,3,0,-12,100}; for (int i = 0; i<10; i++) { Console.Write("{0} ",a[i]); } Array.Sort(a);//对数组进行排序 Array.Clear(a,0,10); //对数组元素置0 Array.Reverse(a); //对数组进行转置 for (int i = a.GetLowerBound(0); i <=a.GetUpperBound(0); i++)//数组的上下标 { Console.Write("{0} ", a[i]); } 5 C#中按值传递和按引用传递 C#中分为值类型变量和引用类型变量,传递值类型参数,可以通过值传递值类型参数,也可以通过 按引用传递值类型参数。 举例按值传递值类型参数如下: Class A {//本例没有实现交换数值的功能 static void swap(int x, int y)//按值传递 { int t; t = x; x = y; y = t; } Public statci void Main() { int a=3,b=5; swap(a,b);//只是传递 a,b的副本,不会改变实参值 Console.WriteLine("{0},{1}",a,b); } 结果:3,5 举例按引用传递值类型参数如下: Class A { static void swap(ref int x,ref int y)// {//按引用传递,调用函数前必须赋值 int t; t = x; x = y; y = t; } Public statci void Main() { int a=3,b=5; swap(ref a,ref b); //传递的不是 a,b的值,是对 a,b的引用,形参 x,y也不是 int类型,是对 int类型的引用 Console.WriteLine("{0},{1}",a,b); 7 } 结果:5,3 C#中引用类型变量不直接包含其数据,包含的是对数据的引用,引用类型数据也能按值传递和按引 用传递。 举例按值传递引用类型数据如下: class passingrefbyval { static void change(int[] arr) { arr[0]=888; // this change affects the original element. arr = new int[5] {-3, -1, -2, -3, -4}; // this change is local. console.writeline("inside the method, the first element is: {0}", arr[0]); } public static void main() { int[] myarray = {1,4,5}; console.writeline("inside main, before calling the method: {0}", myarray [0]); change(myarray); console.writeline("inside main, after calling the method: {0}", myarray [0]); } } 输出: inside main, before calling the method: 1 inside the method, the first element is: -3 inside main, after calling the method: 888 在此情况下,myarray 是一个指向数组的引用,将向方法传递指向 myarray 的引用的一个副本。 我们可以这么理解,一开始 myarray 是数组的引用,通过传递,使 arr 也称为数组的引用,那么这时候 myarrayh和 arr都是同一个数组的引用,所以 change方法中 arr[0]=888;语句改变了这个数组的第 一个元素值,但是 arr = new int[5] {-3, -1, -2, -3, -4};语句分配了新的内存空间,使 arr成为新的一个数组的 引用,所以接下来在 change方法中对 arr引用的数组的操作和myarray引用的数组无关,不会影响它 的值,其实在本例中创建了两个数组,一个在Main方法中,一个在 change方法中。 举例按引用传递引用类型数据如下: 本示例除在方法头和调用中使用 ref 关键字以外,其余和上例相同。 class passingrefbyval { static void change(ref int[] arr) { arr[0]=888; arr = new int[5] {-3, -1, -2, -3, -4}; console.writeline("inside the method, the first element is: {0}", arr[0]); } public static void main() { int[] myarray = {1,4,5}; console.writeline("inside main, before calling the method: {0}", myarray [0]); change(ref myarray); console.writeline("inside main, after calling the method: {0}", myarray [0]); } } 输出: inside main, before calling the method: 1 inside the method, the first element is: -3 inside main, after calling the method: -3 我们可以这么理解,由于是使用 ref 按引用传递,所以传递是引用 myarray 本身,也可以说 arr 就 是myarray,都是原始数组的引用,在 change方法中,有语句 arr = new int[5] {-3, -1, -2, -3, -4}; 8 使得 arr引用重新指向另一个数组,由于 arr就是myarray,所以此时myarray也指向另一个数组,所 以对 arr的操作就是对myarray的操作。 观察下面对于字符数组的交换: public class C { static void swap1(string x, string y)//按值传递 { string t; t = x; x = y; y = t; } static void swap2(ref string x, ref string y)//按引用传递 { string t; t = x; x = y; y = t; } public static void Main() { string s1="abc"; string s2="efg"; swap1(s1, s2); Console.WriteLine("{0},{1}",s1,s2); swap2(ref s1,ref s2); Console.WriteLine("{0},{1}",s1,s2); Console.ReadLine(); } } 输出: abc,efg efg,abc 6 字符串 C++处理字符串,可以使用字符数组、string类或指针来操作。如下: char c1[]=”hello!”; char c2[10]; string s1,s2; s2=c1; cin>>s1; cin>>c2; cout<总结
: (1) 抽象方法是隐式的虚方法 (2) 只允许在抽象类中使用抽象方法的声明 (3) 抽象方法声明没有方法体,以分号结束,实现由一个重写方法提供,该重写方法是一个非抽象类的成 员 (4) 在抽象方法声明中使用 static或 virtual关键字都是错误的 (5) 除了声明和调用语法上不同外,抽象属性的行为和抽象方法一样 (6) 在静态属性上使用 abstract修饰符是错误的 (7) 在派生类中,通过使用 override修饰符属性声明可以重写抽象类的继承属性 (8) C++中如果类中定义了纯虚函数,那么这个类就为抽象类,C#中抽象类必须用 abstract 修饰符定 义,而且抽象类中允许包含非抽象成员,但不是必须的。如: abstract class A { public abstruct void F(); } abstract class B:A { public void G(){} } class C:B { public override void F() {//F的具体实现代码 } } (a) 抽象类 A提供了一个抽象方法 F。类 B从抽象类 A中继承,并且又提供了一个方法 G; (b) 因为 B中并没有包含对 F的实现,所以 B也必须是抽象类。类 C是从类 B中继承,类中重载了抽 象方法 F,并且提供了对 F的具体实现,则类 C允许是非抽象的。 7.1 抽象类的构造函数 不要在抽象类中定义公共的或受保护的内部构造函数,因为 public 和 protected internal的构造函 数是用于能进行实例化的类型,由于任何情况下抽象类都不能实例化,应在抽象类中定义一个 protected 或 private的构造函数,如果在抽象类中定义一个 protected的构造函数,则在创建派生类实例时,基类 可执行初始化任务。 我们把前述例子中虚方法实现多态的例子也可以改为抽象方法实现,如下: abstract class Employee { protected string name; protected Employee() {} 11 protected Employee(string _name) { name = _name; } public abstract void StartWork(); } class Manager:Employee { public Manager(string name):base(name) {} public override void StartWork() { Console.WriteLine(name+"分配工作"); } } class Saler:Employee { public Saler(string name):base(name) {} public override void StartWork() { Console.WriteLine(name+"推销产品"); } } class Technolgy : Employee { public Technolgy(string name) : base(name) { } public override void StartWork() { Console.WriteLine(name+"技术开发"); } } class Program { static void Main(string[] args) { Employee[] emp=new Employee[3]; emp[0] = new Manager("张三"); emp[1] = new Saler("李四"); emp[2] = new Technolgy("王五"); foreach(Employee e in emp) { e.StartWork(); } Console.ReadLine(); } } 这样使用抽象类同样实现了多态,那么在实现多态机制时,什么时候使用虚方法实现什么时候使用抽 象类实现呢?(抽象类和虚方法的选择)一般来说,如果多态中各子类要实现一些共同功能时,倾向于使用 虚方法,可以把共同功能放在基类中,使用 base关键字调用,如果各子类实现功能不一样,那么可以使 用抽象类来构成多态机制。 虚方法和抽象方法的区别: (1) 虚方法有一个实现部分,并且为派生类提供覆盖的选项,相反,抽象方法没有实现部分,强制派生类 覆盖方法(否则派生类不能成为具体类) (2) 抽象方法只能在抽象类中声明,虚方法则不是 (3) 抽象方法必须在非抽象派生类中重写,虚方法则不必 (4) 抽象方法声明时不能有方法体,虚方法则可以 (5) C#用 abstract关键字来修饰一个方法,就可以将这个方法声明为纯虚的。自然,这个方法所属的类 也必须同时被标记为抽象类。请注意,抽象方法不能有任何实现代码。C++中用 virtual 函数名(参 12 数表)=0来声明纯虚函数,带有纯虚函数的类称为抽象类。 8 C#类访问控制修饰符 访问控制修饰符 类内部 子类 程序集内 程序集外 Default(默认为 private) 可以访问 × × × Public 可以访问 可以访问 可以访问 可以访问 Private 可以访问 × × × Protected 可以访问 可以访问 × × Internal 可以访问 可以访问 可以访问 × Protected internal 可以访问 可以访问 可以访问 × Default 指的是没有修饰符,它和 private 是一样的,也就是说成员默认省略修饰符则为 private, 带 protected修饰符的只有类内部和子类中可以访问,internal和 protected internal的区别是:如果 子类和父类不在同一个程序集中,那么子类不能访问 internal 修饰符成员,但是可以访问 protected internal成员。 如果类前面省略访问控制修饰符,则为 internal修饰。 9 C#中的类型转换 C#中有显式和隐式类型转换,不同类型直接赋值就是隐式类型转换,如: int a=5;//int是 int32位数值 long b=7;//long是 int64位数值 b=a;//把小范围数赋给大范围数,不会出错 a=b;//反过来隐式转换,编译通不过 如果我们采用显式转换,如: int a=5; long b=7; b=(long)a;//把小范围数赋给大范围数,不会出错 a=(int)b;//显式转换,a=7,成功 但如果 b的值超过了 a所能表示的范围,那么会发生溢出,如: int a; long b=70000000000; a=(int)b;//溢出,但不会报错,a的值也不等于 b 怎么样在类型转换发生溢出等异常时报错呢?我们可以用 checked和 unchecked关键字,如: int a; long b=70000000000; a=checked((int)b);//溢出,报异常 我们可以捕捉异常,如下: int a; long b=70000000000; try { a=checked((int)b); } catch(System.OverflowException) { MessageBox.Show(“发生溢出!”); return;//发生溢出后程序不再往下执行 } 如果要对多个语句进行检查,可以使用 checked语句,如: try 13 { Checked { a=(int)b;//可以写多个语句 } } catch(System.OverflowException) { MessageBox.Show(“发生溢出!”); return;//发生溢出后程序不再往下执行 } 如果写代码时允许发生溢出,最好把语句放到 unchecked块中。 C#中也可以进行引用类型的转换,如: class Fruit { } class Apple:Fruit { public int i=5; } class Test { Static void Main() { Fruit f=new Apple();//正确,形象理解:苹果也是水果 Type t=f.GetType();//返回 f的类型名 Console.WriteLine(t.FullName);//输出 Apple Apple
/
本文档为【C#与C++异同】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索