美文网首页工作生活
C++程序员光速入门C#(二):流程控制、函数、类

C++程序员光速入门C#(二):流程控制、函数、类

作者: 饮茶先啦靓仔 | 来源:发表于2019-07-09 23:31 被阅读0次

    Jacob的C++程序员光速入门C#系列:
    C++程序员光速入门C#(一):总览、数据类型、运算符
    C++程序员光速入门C#(二):流程控制、函数、类
    C++程序员光速入门C#(三):继承、泛型、异常

    一.流程控制

    1.if/if..else语句

    这个语句和C++没有区别

    2.switch

    选择分支语句和C++用法也类似,但是C#中对它的格式更加严格了,每个case语句后面都必须跟上break,不然就是错误的语句。但是也有例外,可以使用臭名昭著的goto语句,跳转到下个case语句,这样编译器是不会报错的。

    当然如果你真的希望有多个条件执行相同语句的话,可以省略写成下面的方法:

    switch (char c) 
    { 
        case ‘ua’: 
        case ‘us’: 
        case ‘en’:string language=”English”;break; 
    } 
    
    2.switch

    C++中的3种循环语句:for , while , do…while 和C#中是相同的 。但是C#中添加了一种循环语句,在对数组的输出方面的使用非常灵活, 它就是:foreach语句 。它的语法格式如下

    foreach (<变量类型> <变量名>  in  <数组名>)  {} 
    

    不管是一维还是多维数组,操作都一样方便,它会自动知道数组的大小对其操作,我们不需要关心它是否会溢出。

    C#中现在增加了交错数组,对它的操作,foreach也可以做到,但格式不同,看下面的例子。

    using System; 
    namespace ConsoleApplication1 
    { 
        class Class1 
        { 
            static void Main(string[] args) 
            { 
               //定义多维数组
                int[,] a1=new int[2,2]{{1,2},{3,4}}; 
                foreach (int b1 in a1) 
                { 
                    Console.WriteLine ("{0}",b1);//对多维数组的操作 
                } 
                //定义交错数组
                int[][] a2=new int[2][]; 
                a2[0]=new int[2]{0,1}; 
                a2[1]=new int[3]{3,4,5}; 
                foreach (int[] b20 in a2) 
                { 
                    foreach (int b21 in b20)
                    { 
                        Console.WriteLine ("{0}",b21);//对交错数组的操作 
                    }
                } 
            } 
        } 
    } 
    

    对于变长数组的如此操作,是因为a2包含的是一个int[]元素,而不是int元素,想想我们之前讲的交错数组也叫数组的数组,其实是2个数组的欠套。

    现在我们再来看一个对foreach的使用:

    //错误例子
    int[,] a1=new int[2,2]{{1,2},{3,4}}; 
    foreach (int b1 in a1) 
    { 
        b1=3;   //错误 !!!
    } 
    

    还记得上面我说的一句话吗?"在对数组的输出方面的使用非常灵活”。也许你已经看出来了,是的,foreach语句只能对数据进行输出,因为它是只读的,不能改变任何数组元素的值。

    Qt5中定义了一个关键字,也叫foreach。它的格式是:
    foreach (varItem , Items) //中间是逗号
    而在C++11标准中,也加了一个foreach,格式是:
    foreach (varItem : Items) //中间是冒号
    跟C#的foreach也是类似的,也不能修改数组元素。

    4.跳转语句

    C#中的跳转语句和C++中一样,有:goto , return , break ,continue 。

    二.函数

    讲到C#的函数,觉得有点别扭,因为现在的所以函数都是包含在一个类当中了,这样就和C++中类的方法的用法相似了,所以在C++中的标准函数需要定义的过程也没有了,这样确切的说函数在C#中已经不复存在了,都变成方法了嘛!

    首先我们来看一下C#方法的使用的例子。

    /***********************C#程序***************************/
    using System; 
    namespace ConsoleApplication1 
    { 
     
        class Class1 
        { 
            static void Main(string[] args) 
            { 
                Write();     
            } 
            static void Write() 
            {    
                Console.WriteLine ("This is Text"); 
            } 
        } 
    } 
    

    需要反复强调的是:C#是一门完全面向对象的语言!如果要以面向过程的思路定义和使用函数的话,就要在一个类中定义一个静态函数,使用static关键字!

    1.参数

    C#的方法和C++中的函数在返回值,参数传递方面是一样的,可变参数,默认参数也都有,唯一的区别是引用的传递。 也许你想起来上面所讲,C#中取消了&操作符,这意味着没有了引用传递了,其实不是没有,而是不用&,改成了ref 。

    class Class1 
    { 
        static void Main(string[] args) 
        { 
            int a=1,b=2; 
            Console.WriteLine ("{0} {1}",a,b); 
            change(ref a,ref b) ; 
            Console.WriteLine ("{0} {1}",a,b); 
        } 
        static void change(ref int a,ref int b) 
        {    
            int c; 
            c=a; 
            a=b; 
            b=c; 
        } 
    } 
    /*******************
    输出结果: 
    1         2 
    2         1 
    ********************/
    

    ref参数需要在函数外初始化传入的变量。out参数则可以不需要初始化。

    int a;
    func(out a);//不会报错,ref参数会报错
    
    2.委托

    委托的概念可以类比C++的函数指针,但某些方面有差别。用法看下如下例子。先看C++的函数指针。

    /**********************C++程序*************************/
    #include <iostream>
    
    using namespace std;
    
    double (*process)(double num1, double num2);
    
    double add(double num1, double num2)
    {
        return num1+num2;
    }
    
    double odd(double num1, double num2)
    {
        return num1-num2;
    }
    
    
    int main()
    {
        bool m_bool=true; 
        double num; 
        if (m_bool) 
        { 
            process = add;
        } 
        else 
        { 
            process = odd;
        } 
        num=process(10,5);  // 结果是15
        cout << "num=" << num << endl;
        
        m_bool=false; 
        if (m_bool) 
        { 
            process = add;
        } 
        else 
        { 
            process = odd;
        } 
        num=process(10,5);  //结果是5
        cout << "num=" << num << endl;
    
        return 0;
    }
    

    很简单的函数指针用法。当然也可以用typedef一个函数指针类型,熟练的C++程序员应该都懂。C++11标准中添加了decltype关键字,也可以用来创建函数指针类型,这里就不再赘述了。

    下面看下C#中的委托。

    /************************C#程序**************************/
    using System; 
    namespace ConsoleApplication1 
    { 
        class Class1 
        { 
            delegate double chooes(double num1,double num2); //声明委托
            static double add(double num1,double num2) 
            { 
                return num1+num2; 
            } 
            static double odd(double num1,double num2) 
            { 
                return num1-num2; 
            } 
            static void Main(string[] args) 
            { 
                chooes process; 
                bool m_bool=true; 
                double num; 
                if (m_bool) 
                { 
                    process =new chooes (add); 
                } 
                else 
                { 
                    process =new chooes (odd); 
                } 
                num=process(10,5); 
                Console.WriteLine ("{0}",num);  //结果是15
                 
                m_bool=false; 
                if (m_bool) 
                { 
                    process =new chooes (add); 
                } 
                else 
                { 
                    process =new chooes (odd); 
                } 
                num=process(10,5); 
                Console.WriteLine ("{0}",num); //结果是5
            } 
        } 
    } 
    

    首先注意到的是delegate关键字。delegate关键字类似与用C++的typedef关键字定义了一个函数指针类型,只有符合函数签名的函数才能被传入。

    需要注意的是,C#中的委托对象是真正的对象,而C++中的函数指针变量只是函数的入口地址

    C#中声明了一个委托对象之后,要用new关键字在堆栈上开辟空间。

    //C#程序
    chooes process; 
    process =new chooes (add); 
    

    而C/C++中的函数指针只是指针,存储的是函数的入口地址。

    //C++程序
    process = add;//process = &add;也可以
    

    所以一旦函数指针指向的内存区域发生改变,就可能发生意料之外的事情。所以C#的委托更加安全。

    还有一点需要注意的是,C#的委托赋值为一个类方法,只要符合函数签名。而C++的函数指针则需要一些额外的声明。看下以下C++程序。

    /*****************C++程序******************/
    #include <iostream>
    
    using namespace std;
    
    class operation
    {
    public:
        double add(double num1, double num2)
        {
            return num1+num2;
        }
    
        double odd(double num1, double num2)
        {
            return num1-num2;
        }
    };
    
    double (operation::* process)(double num1, double num2);
    
    int main()
    {
        bool m_bool=true; 
        double num; 
        operation *op = new operation;
        if (m_bool) 
        { 
            //跟普通函数不一样,一定要加&
            process = &operation::add;
        } 
        else 
        { 
            process = &operation::odd;
        } 
        //一定要加*然后括起来
        num=(op->*process)(10,5);
        cout << "num=" << num << endl;
        
        m_bool=false; 
        if (m_bool) 
        { 
            process = &operation::add;
        } 
        else 
        { 
            process = &operation::odd;
        } 
        num=(op->*process)(10,5);
        cout << "num=" << num << endl;
    
        return 0;
    }
    
    

    可以看到,声明函数指针变量时签名要加上类的作用域名。大概是C++编译器会将类成员函数的签名加上一个this指针参数,如 double (*)(operation:: this,double num1, double num2);.。而C#的委托就不需要这样了,直接new就完事了。

    三.类与对象

    对于面向对象语言来说,类是重头戏,C#中类的使用和C++中类似,在细节方面有些差别。

    class myclass:mybasicclass   //注意基类不需要写限定符 
    { 
        private int a; 
        public int num() 
         { 
             return 0; 
         } 
    } 
    public class mybasicclass 
    { 
    } 
    

    这里写出了一个类的限定符public,C#还有的限定符:

    internal:只能在当前工程中使用,当class前缺省限制符时就默认为此限制符
    public:可以在任何工程中使用
    abstract(抽象的):类不能实例化,只能继承,可以有抽象成员
    sealed (密封的):不能继承

    关键字能够相互组合。

    我们看到,C#中没有私有继承之类的概念,类成员的访问控制权限在继承过程中不会改变。(事实上C++也很少用到私有继承)。

    首先需要知道一点,C#中只能有一个基类,它不支持多重继承。在前文也已经说过,object类是C#中最高的类,如果一个类没有基类,它就隐式的继承了object类,在类中都可以使用object提供的方法.。

    也许你还记得在C++中,类里面定义的方法的具体实现是可以在类的外部实现的,但在C#中已经不允许,C#规定所有的代码都必须在类中。

    从上面的例子中还能发现一个不同点就算是:在类定义结束的时候没有了分号。

    从现在开始,从细节上阐述C#的不同用法:

    1. 在C++的名词成员变量,在C#中称为字段,字段在C++中不能初始化,但在C#中可以直接初始化,成为默认值。
    //这样在C++中是会报错的
    class Myclass
    {
        public double x=10, y=10086, z=131;
        public Myclass()
        {
        }
    }
    
    1. 构造函数:C#添加了一种静态构造函数
    public class mybasicclass 
    { 
        static int a;
        static mybasicclass() 
        { 
            a=10086;
        } 
    } 
    

    它不需要限定符,它的作用是在程序运行时对静态变量进行赋值。它什么时候调用是不能确定的,CLR接手了这个过程,程序员不要瞎操心(对,说的就是啥都想管的C++程序员。。)。

    3.析构函数(终结器):它和C++的概念已经完全不同了,因为C#内部有了内存自动收集机制,能够对不用的内存进行回收,所以不需要在析构函数中释放内存了,一般情况下不需要重写析构函数,也不能显式调用析构函数。如果想像C++一样自己管理内存,可以让类去实现一个叫做IDispoable的接口。接口的概念会在以后提到。

    4.虚函数:C#的虚函数同C++在语法上有区别,但是含义还是一样的。C#中基类的虚拟函数使用关键字virtual,在派生类中使用override

    5.运算符重载:C#中运算符重载和C++原理基本相同,区别在于有些运算符C#已经不允许重载了。不能重载的运算符有:-= , += ,/= ,*= ,%=, 按位赋值运算符(&= ……) ,布尔运算符(&&……),赋值运算符(=)。另外语法也略有不同。看下C#的例子。

    namespace MyVector
    {
        struct Vector
        {
            public double x, y, z;
    
            //构造函数,没区别
            public Vector(double x,double y,double z)
            {
                this.x = x;
                this.y = y;
                this.z = z;
            }
    
            //复制构造函数,和C++也莫得区别
            public Vector(Vector rhs)
            {
                this.x = rhs.x;
                this.y = rhs.y;
                this.z = rhs.z;
            }
    
            //运算符重载
            //必须是public static类型,且二元运算符有两个参数,一个左值一个右值
            public static Vector operator + (Vector lhs, Vector rhs)
            {
                Vector result = new Vector(lhs);
                result.x += rhs.x;
                result.y += rhs.y;
                result.z += rhs.z;
    
                return result;
            }
        }
    }
    

    语法格式是 public static 返回类型 operator 运算符 (参数)。

    可以看到,C#中对于运算符重载的语法比较合理,对于二元运算符的重载,参数中包括了左值和右值。C++中如果出现左值是其他类型的情况(比如说 int + Vector ),就要用到友元函数的特性,语法感觉十分丑陋。。。

    同时在C#中还有一个硬性的规定,比如重载了运算符> 就必须重载< ,重载了== ,就必须重载 != ,重载了>=就要重载<=,反之亦然。也就是说,比较运算符要成对地重载,不然编译器就会报错。


    可重载和不可重载的运算符

    6.属性:C#中属性的概念相当于C++中给数据成员定义它的get方法和set方法。语法如下。

    private int age;
    public int Age
    {
        get
        {
            return age;
        }
        set
        {
            age = value;
        }
    }
    

    get和set称为访问器。当我们想让这个属性不能被修改时,可以定义只读属性,不要set访问器就可以了。

    private int age;
    public int Age
    {
        get
        {
            return age;
        }
    }
    

    如果访问器没有任何的逻辑,就可以使用自动实现的属性,语法如下。

    private int age;
    public int Age { get; set; }
    //public int Age { get; } //只读
    //public int Age { get; private set; } //不能外部修改
    

    7.其他:partial关键字允许把类、结构、方法和接口的定义放在多个文件中,称为部分类。static关键字也可以修饰类,称为静态类。如果类中出现非静态成员,编译器就会报错。

    暂时先写到这里,关于继承以及其它高级特性下一篇会讲到。说实话原博客的内容一方面有一些错误,需要大量的翻阅资料去确认和修正;另一方面这么多年过去,C#和C++都成长了不少,要讲的内容实在是有点多,而笔者的能力实在有限。只求少点错误,别耽误可爱的网友吧😭。

    Reference:
    QT中foreach的使用
    C++程序员快速学习C#---(二)
    C# vs C++之一:委托 vs 函数指针
    被淡忘的C#析构函数
    C# 运算符重载
    C#高级编程 ——Christian Nagel & Jay Glynn & Morgan Skinner

    相关文章

      网友评论

        本文标题:C++程序员光速入门C#(二):流程控制、函数、类

        本文链接:https://www.haomeiwen.com/subject/tsfhhctx.html