一,为什么绝不定义继承而来的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都不因该改变其行为。这是一份继承接口和强制实现。