zoukankan      html  css  js  c++  java
  • C++基础:虚函数、重载、覆盖、隐藏<转>

    转自:http://www.2cto.com/kf/201404/291772.html

    虚函数总是跟多态联系在一起,引入虚函数可以使用基类指针对继承类对象进行操作!

    虚函数:继承接口(函数名,参数,返回值),但是实现不继承(函数体)

    非虚函数:继承接口,也继承实现;

    1)虚析构函数(当一个类打算作为基类使用时候,其析构函数必须是虚函数)

    构造函数可以为虚函数吗? 不可以,在生成对象的时候,必须向编译器明确指定要生成什么类型的对象,因而不存在虚函数的问题;只有当对象已经存在,我用什么接口去操作它的问题;

    例子:

    class A
    {
    public:
    A();
    virtual~A();
    };
    class B: public A
    {
    public: 
    B();
    ~B();
    };
    int main()
    {
    A *pA=new B; //调用B的构造函数
    delete pA; 
    return 0; 
    }

    这里通过指针A去调用B类的析构函数,但是如果 ~A()不是虚析构函数,就不存在多态,就会去调用A中的析构函数,最后结果可能是B对象中的A成分被析构了,其他 还残留;所以当一个类作为基类时候,其析构函数必须是虚函数,这样防止出现析构不完全的情况;

    2)当虚函数出现在构造、析构函数中时(即在构造函数中调用虚函数),函数退化为普通函数。为什么? <>中条款

    例子:(转自<>中条例9)

    class Transaction

    {
    public:
    Transaction();
    virtual void LogTransaction() const = 0;
    // ...
    };
    Transaction::Transaction()
    {
    // ...
    LogTransaction();
    }
    class BuyTransaction : public Transaction

    {
    public:
    virtual void LogTransaction() const;
    // ...
    };

    这里调用BuyTransaction的构造函数,因为BuyTransaction是继承类所以先调用基类的构造函数,此时派生类独有的那部分还未初始化,<>中这么解释:“这个对象内的BuyTransaction成分还未初始化,最安全的办法是当他不存在,对象在derived成分还未初 始化时该对象不会成为一个derived对象”所以Transaction 的构造函数会调用基类的 LogTransaction() const;而且此处是个纯虚函数,会报错

    3)虚函数与覆盖,重载,隐藏

    重载首先出现在非继承关系当中,当同一个类中,两个函数的参数不同,名字相同,返回值类型无所谓(函数返回值不作为重载的参考,因为函数调用时候不出现返回值);

    注意两个函数可以仅仅因为const与非const的差别来实现重载;

    虚函数与覆盖均出现在一个继承体系中,覆盖针对的是普通函数,当父类子类中出现同名(相同返回值,相同参数,相同函数名)要求绝对一致

    虚函数在父类子类中,首先在父类中声明该函数为virtual,那么子类可以重新定义该函数的实现,这里主要涉及多态,就是覆盖的情况加上virtual,通过指针或者引用实现多态;

    隐藏显得简单粗暴,在继承类中只要出现于父类同名(只要求同样的函数名/变量名,其他返回值,virtual 非virtual不管)的函数,则用对象调用同名函数时候,基类对应的同名函数、变量隐藏,如果要访问其父类的同名成员应该明确使用 基类名::成员来访问;

    例子:

    class A
    {
    public:
    virtual ~A(){};
    void process(int i,char c); //重载,编译期间即可确定该调用哪个函数
    char process(double d,int c); //只要函数名相同,返回无所谓,参数不同
    void process(int i); 
    void process(int i)const; // 此处相当于void process(const A *this,int i);
    void process(consttint i); //这里仅仅因为const属性不同即可实现重载
    virtual int foo(int, char){...}; 
    int foo2(){};
    void foo3(int,int ){};
    }
    class B: public A
    {
    public:
    int foo(int ,char){...}; //虚函数,这里函数接口要严格一致(大部分编译器要求返回值也要一致)
    int process(){...}; //隐藏基类函数 
    int foo2(){}; // 这里不含虚属性,会覆盖
    int foo3(){return 0;}; //会隐藏A类的foo3
    }
    int main()
    {
    B b;
    A *pA=&b;
    pA->foo2(); //这里调用A类的foo2();不涉及多态;
    pA->foo(3,'c'); //B类foo() 涉及多态
    }

    总结:

    1)覆盖与虚函数是一对兄弟,要求函数的返回值,函数名,参数严格一致,虚函数是覆盖加上virtual的情况;

    2) 隐藏是覆盖的推广,覆盖是隐藏的特例,只要求函数名一样,其他不管,在继承体系中,子类的同名函数会将父类的同名函数隐藏;

    3)当使用指针时候,指向基类的指针会根据实际对象的类型,选择相应的虚函数执行,如果派生类没有重新定义基类的虚函数,那么依然执行基类的虚函数;

    4)当不存在虚函数的情况下,使用基类的指针,不会下降到派生类中去搜索函数;所以virtual属性相当于告诉基类指针:当执行我时,请到相应对象中搜索对应的虚函数;

    5)纯虚函数所在的类是抽象类,不能实例化,定义了纯虚函数意味着这个函数只能为父类,其负责定义接口而不负责实现;

    6)当使用对象来调用相应的函数时候,主要考虑的是对基类同名函数的隐藏(包括覆盖),而不需要考虑多态;

    7)注意构造析构函数中不能调用虚函数,当执行派生类的构造函数时候,先构造的是其基类成分,再执行派生类成分的构造,虚函数此时无意义;

    8)c++是个细节非常多,非常复杂的语言;

  • 相关阅读:
    Java的字节流,字符流和缓冲流对比探究
    团队作业4:第七篇Scrum冲刺博客(歪瑞古德小队)
    团队作业4:第六篇Scrum冲刺博客(歪瑞古德小队)
    团队作业4:第五篇Scrum冲刺博客(歪瑞古德小队)
    团队作业4:第四篇Scrum冲刺博客(歪瑞古德小队)
    EACCES: permission denied,mkdir … npm install 安装依赖问题解决
    100% 展示 MySQL 语句执行的神器-Optimizer Trace
    关于冲击响应的通俗理解
    struct中的对齐准则
    NIO实践-HTTP交互实现暨简版Tomcat交互内核
  • 原文地址:https://www.cnblogs.com/syx-1987/p/4084671.html
Copyright © 2011-2022 走看看