• 易混淆概念总结


    (一)虚方法的重写与普通方法的隐藏的区别;抽象方法和虚方法有什么区别?
    两者都可实现对基类方法的重写 ,虚方法在基类中virtual ,在派生类用override关键字修饰,而普通方法的隐藏在基类不用什么修饰符,而在派生类中用new关键字进行重写 ,我想问,它们同样是方法的重写, c#为什么会有两种方法重写方法,这样设计不是重复了呢, 这怎么理解呢?
    不重复,首先如果基类里面的virtual方法被子类继承之后,用override重写之后,那么子类里面就只有一个该方法名的方法了。而另一个普通的,就是在子类里面直接通过new 来新建一个方法那叫重载(多态分为重写和重载),此时子类里面包含两个同名方法,一个是父类继承的,一个是自己通过new 重载的。只不过,父类继承的会隐藏了

    抽象方法和虚方法有什么区别?
    抽象方法是只有定义、没有实际方法体的函数,它只能在抽象函数中出现,并且在子类中必须重写;虚方法则有自己的函数体,已经提供了函数实现,但是允许在子类中重写。
    重写的子类虚函数就是被覆盖了。


    2 成员函数的重载、覆盖与隐藏
    成员函数的重载(overload)、覆盖,重写(override)与隐藏(hide)很容易混淆,C++程序员必须要搞清楚概念,否则错误将防不胜防。
     
    8.2.1 重载与覆盖
    成员函数被重载的特征:
    (1)相同的范围(在同一个类中);
    (2)函数名字相同;
    (3)参数不同(类型、个数);
    (4)virtual关键字可有可无。
    覆盖,重写是指派生类函数覆盖基类函数,特征是:
    (1)不同的范围(分别位于派生类与基类);
    (2)函数名字相同;
    (3)参数相同;
    (4)基类函数必须有virtual关键字。


    8.2.2 令人迷惑的隐藏规则
    本来仅仅区别重载与覆盖并不算困难,但是C++的隐藏规则使问题复杂性陡然增加。这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
    (1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
    (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

    (二)虚函数

    定义

    编辑
    简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。下面来看一段简单的代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #include<iostream>
    using namespace std;
    class A
    {
        public:
            void print()
            {
                cout<<"This is A"<<endl;
            }
    };
     
    class B : public A
    {
        public:
            void print()
            {
                cout<<"This is B"<<endl;
            }
    };
     
    int main()
    {
        //为了在以后便于区分,我这段main()代码叫做main1
        A a;
        B b;
        a.print();
        b.print();
        return 0;
    }

    输出结果

    编辑
    分别是“ThisisA”、“ThisisB”。
    通过class A和class B的print()这个接口,可以看出这两个class因个体的差异而采用了不同的策略,但这是否真正做到了多态性呢?No,多态还有个关键之处就是一切用指向基类指针或引用来操作对象。那现在就把main()处的代码改一改。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int main()
    {
        //main2
        A a;
        B b;
        A *p1 = &a;
        A *p2 = &b;
        p1->print();
        p2->print();
        return 0;
    }
    运行一下看看结果,结果却是两个This is A。
    问题来了,p2明明指向的是class B的对象但却是调用的class A的print()函数,这不是我们所期望的结果,那么解决这个问题就需要用到虚函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class A
    {
        public:
            virtual void print(){cout<<"This is A"<<endl;}
    };
    class B : public A
    {
        public:
        void print(){cout<<"ThisisB"<<endl;}
    };
    毫无疑问,class A的成员函数print()已经成了虚函数,那么class B的print()成了虚函数了吗?回答是Yes,我们只需在把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。所以,class B的print()也成了虚函数。那么对于在派生类的相应函数前是否需要用virtual关键字修饰,那就是你自己的问题了(语法上可加可不加,不加的话编译器会自动加上,但为了阅读方便和规范性,建议加上)。
    现在重新运行main2的代码,这样输出的结果就是This is A和This is B了。
    现在来消化一下,我作个简单的总结,指向基类指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。
     
    作用:可以让成员函数操作一般化,用基类的指针指向不同的派生类的对象时,
    基类指针调用其虚成员函数,则会调用其真正指向对象的成员函数,
    而不是基类中定义的成员函数(只要派生类改写了该成员函数).
    若不是虚函数,则不管基类指针指向的哪个派生类对象,调用时都
    会调用基类中定义的那个函数.
      
     



    (三)接口与抽象类的区别
    1.简单来说,
    接口是公开的,里面不能有私有的方法或变量,是用于让别人使用的,而抽象类是可以有私有方法或私有变量的,

    2.另外,实现接口的一定要实现(重写)接口里定义的所有方法,而实现抽象类可以有选择地重写需要用到的方法,一般的应用里,最顶级的是接口,然后是抽象类实现接口,最后才到具体类实现。

    3.还有,接口可以实现多重继承,而一个类只能继承一个超类,但可以通过继承多个接口实现多重继承,接口还有标识(里面没有任何方法,如Remote接口)和数据共享(里面的变量全是常量)的作用.
    4.抽象方法是必须实现的方法。就象动物都要呼吸。但是鱼用鳃呼吸,猪用肺呼吸。
    动物类要有呼吸方法。怎么呼吸就是子类的事了。
    现在有很多讨论和建议提倡用interface代替abstract类,两者从理论上可以做一般性的混用,但是在实际应用中,他们还是有一定区别的。抽象类一般作为公共的父类为子类的扩展提供基础,这里的扩展包括了属性上和行为上的。而接口一般来说不考虑属性,只考虑方法,使得子类可以自由的填补或者扩展接口所定义的方法,就像JAVA王子所说的事件中的适配器就是一个很好的应用。
    用一个简单的例子,比如说一个教师,我们把它作为一个抽象类,有自己的属性,比如说年龄,教育程度,教师编号等等,而教师也是分很多种类的,我们就可以继承教师类而扩展特有的种类属性,而普遍属性已经直接继承了下来。
    而接口呢~还是拿教师做例子,教师的行为很多,除了和普通人相同的以外,还有职业相关的行为,比如改考卷,讲课等等,我们把这些行为定义成无body的方法,作为一个集合,它是一个interface。而教师张三李四的各自行为特点又有不同,那么他们就可以扩展自己的行为body。从这点意义上来说, interface偏重于行为。
    总之,在许多情况下,接口确实可以代替抽象类,如果你不需要刻意表达属性上的继承的话。

     

    (四)如何让子类不能修改,重写父类的方法

    1.将父类中的方法定义为静态方法

    2.把父类定义为final类

  • 相关阅读:
    edgeR
    R中的运算符,条件语句,控制语句
    library-type:fr-unstanded vs fisrt-stand vs second-stanrd
    R的几个基础函数
    HTseq-count
    HISAT2的运用
    shell的符号总结
    python-tuple
    python -List
    win10 ubuntu18.0 LTS双系统安装
  • 原文地址:https://www.cnblogs.com/to-creat/p/4944986.html
Copyright © 2020-2023  润新知