2021-9-9(_3一文读懂避免遮掩继承而来的名称《using和转交技术》以及绝不定义继承的non-virtual函数,区分接口继承(pure virtual)和实现继承(virtual))

一,为什么绝不定义继承而来的non-virtual函数
先看一个错误的例子:
class B
{
public:
viod mf();

};

class D:public B
{
public:
void mf();
};

首先,这里显然违背了避免遮掩继承而来的名称,如果用pd->mf() D:mf,而pb->mf() B::mf
显然D中的mf对继承而来的mf()进行遮掩了,也就是覆盖。这是一个不好的做法,(解决办法:using声明和转交技术见下文);

其次,这里是public继承(“is
a"),意味着每一个D对象就是一个B对象,如果重新定义一个与B::mf不同的mf,设计就出现了矛盾,”每一个D对象就是一个B对象不为真“因为同时遮掩了父类的mf,既然如此就不该用public继承B

36: 故绝不重新定义继承而来的non-virtual函数

二,如何避免遮掩继承而来的名称

如上述所示,如果你真的继承了base class 的同名non-virtual函数,那么怎么处理?答案是使用using
声明告知编译器你使用了父类的同名non-virtual函数,或者使用转交技术(在子类同名函数调用父类同名函数达到转交作用)

eg:一个例子具体看看这两者技术的使用:

#include<iostream>
#include<string>
using namespace std;

class Base
{
private:
    int x;
public:
    Base(/* args */){cout<<"Base()"<<endl;}
    virtual void mf1()=0;
    virtual void mf1(int ) {cout<<"this is mf1(int) of Base"<<endl;}
    virtual void mf2() {cout<<"this is mf2() of Base"<<endl;} 
    void mf3(){cout<<"this is mf3() of Base"<<endl;}
    void mf3(double c) {cout<<"this is mf3(double ) of Base"<<endl;}
    virtual void mf(int x ){cout<<x<<"this is mf(int)"<<endl;}
    virtual void mf(){cout<<"this is mf() of Base"<<endl;}
    ~Base(){cout <<"~Base()"<<endl;}
};

class Derived: public Base
{
private:
  
public:
    virtual void mf1(){cout<<"this is mf1() of Derived"<<endl;}
    void mf3(){cout<<"this is mf3() of Derived"<<endl;}
    //void mf3(){ Base::mf3();}//法二,(调用基类同名函数)转交技术
    void mf4(){cout<<"this is mf4() of Derived"<<endl;}  
    //法一,不使用uisng 声明违法了is a 因为base 的同名mf1()以及mf1(int)被覆盖(只检查同名函数来实现覆盖)
    
    using Base::mf1;//会对当前的mf3覆盖,想让他可见可使用Derived::mf3()单继承一个non-virtual本身就是一个做误的做法,见上文提到故绝不重新定义继承而来的non-virtual函数
    using Base::mf3;
};

class CpDerived: private Base
{
public:
    //我们只想使用Derived的mf(int)版本
    virtual void mf1()
    {
        cout<<"this is mf1() of CpDerived"<<endl;
    }
        
    virtual void mf(int x)
    {
        Base::mf(x);             //只能使用转交函数技术
       cout<<"this is mf(int) imlement of CpDerived"<<endl;
    }
};
test.cpp
#include"Base.h"
#include<cstdio>
int main()
{ /*
    //uisng 声明测试
    Derived d;
    int x=1;
    double c=1.0;
    d.mf1();
    d.mf1(x);
    d.mf2();
    d.mf3(c);   
    d.mf4();
    */
   CpDerived cd;
  // cd.mf();  Bade::mf()被隐藏了错误
  // 转交技术测试
    cd.mf1();
    cd.mf(2);
    getchar();
    return 0;
}

大家可以自行赋值代码进行测试,

这里值得注意的是,当你在复合的关系中使用了private的继承方式的话,而且父类有多个同名函数的情况下,而你只想使用其中某一个函数的话,(uisng会让继承而来的同名函数会被覆盖导致不可见)你只能使用转交技术。

term 33: 为了让遮掩的代码可见,可以使用注释中的using 声明或者转交函数。

三,区分接口继承和实现继承。

3.1接口继承(纯虚函数):
声明一个pure函数目地是为了让derived classes 只继承接口
3.2实现继承(一般的虚函数):
目地是为了让derived classes 继承该函数的接口和缺省实现
3.3non-vitual函数:
目地是为了令derived classes继承函数的接口和一份强制实现。(难以理解?下面举例详细说明)

#include<iostream>
#include<string>
using namespace std;

class Airport//机场类表示目的地
{
public:
  const void destinatonA()const
   {
       cout<<"目地B"<<endl;
       
   }

   const void destinatonC()const
   {
       cout<<"目地C"<<endl;
       
   }

};

class Airplane//抽象飞机
{
private:
    /* data */
public:
    Airplane(/* args */){}
    virtual void fly(const Airport&destination) =0;
    // virtual void fly(const Airport&destination) const=0;//const的有无也是一个重载
    virtual ~Airplane(){}
protected:
    //第二种方式,使用第一种的话这个可注释二选一
    const void defaultDestination()const 
    {
         cout<<"目地D"<<endl;
       

    }
};

/*   这里表明纯虚函数也可以有定义
void Airplane::fly(const Airport& destination)const
{
    cout<<"不指定出发的目的地就去D"<<endl;
    destination.destinatonA();
}
*/
class ModdelA:public Airplane
{
private:
    /* data */
public:
    ModdelA(/* args */):Airplane(){}
    void fly(const Airport&destination)
    {
        destination.destinatonA();
    }

    ~ModdelA(){}
};

class ModdelB:public Airplane
{
private:
    /* data */
public:
    ModdelB(/* args */):Airplane(){}
    void fly(const Airport&destination)
    {
        destination.destinatonB();
    }

    ~ModdelB(){}
};

class ModdelC:public Airplane
{
private:
    /* data */
public:
    ModdelC(/* args */):Airplane(){}
    //客户忘了定义fly() cilent will remind you 
    void fly(const Airport&destination)
    {
        defaultDestination();
    }
    ~ModdelC(){}
};



cilent.cpp

#include"Shap.h"
#include"Airplane.h"
#include<cstdio>
#include<memory>

int main()
{

    auto_ptr<Shape>pr(new Rectangle("正方形"));
    auto_ptr<Shape>pe(new Ellipse("圆"));
    pr->draw();
    pr->error();//Rectangle的error是个默认错误没有定义

    pe->draw();
    pe->error();
    cout<<"pid: "<<pe->objectID()<<endl;


    Airport airport;
    auto_ptr<Airplane>pa(new ModdelA());
    auto_ptr<Airplane>pc(new ModdelC());
    pa->fly(airport);
    pc->fly(airport);
    getchar();
    return 0;
}`

上面有ABC三种不同的飞机飞往自己的目的地,可以看到fly()被声明为纯虚函数,说明每一飞机必须有自己的实现,如果忘记定义编译器会提醒你,同时,为了实现默认的出发地点,上面使用了两种技术(缺省实现):
一,保护成员函数,defaultDestination()为保护成员,如果不指定地点的话,我们就在各自的fly()调用defaultDesinaton();
二,纯虚函数实现自己的定义+转交技术
//父类的纯虚函数定义:
void Airplane::fly(const Airport& destination)const
{
cout<<“不指定出发的目的地就去D”<<endl;
destination.destinatonA();
}
//那么在子类的定义中:

void fly(const Airport&destination)
{
Airplane::fly(destination);
}
3.3non-vitual函数的理解:
eg:

#include<iostream>
#include<string>
using namespace std;
class Shape
{
private:
        cout<<"这可能是一个默认的错误在Shape()中 "<<endl;
    }

    string objectID()const
    {
        return m_pid;
    }
    
    virtual ~Shape(){}
};

class Rectangle: public Shape
{
private:
    string m_pid;

public:
    Rectangle(string pid):Shape("形状是矩形"),m_pid(pid) {}
    void draw()const
    {
        cout<<"绘制一个矩形"<<endl;
    }
 ~Rectangle(){}  
};


class Ellipse: public Shape
{
private:
    string m_pid;

public:
    Ellipse(string pid):Shape("形状是圆"),m_pid(pid) {}
    void draw()const
    {
        cout<<"绘制一个圆"<<endl;
    }


     void error()const
    {
        cout<<"绘制一个圆方法错误"<<endl;
    }

   ~Ellipse(){}
};

objectID()函数显然是non-virtual函数,但是我们在子类绝对没定义他的实现,这是因为我们的子类一直都会使用这个行为,此函数带表各个子类总是采用这种相同的计算方法,该方法有shape::objectID的定义决定任何derived
class都不因该改变其行为。这是一份继承接口和强制实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值