几种继承方法
类型之间的public继承–is a(derived class is a base class.)
类型之间的复合关系–has a或根据某物实现出
class address{...}; class phonenumber{...}; class person{ public: ... private: std::string name; address theaddress; phonenumber thephone; };
private继承:意味着根据某物实现出(如果D以private形式继承B,意思是D对象根据B对象实现而得)
private继承意味只有实现部分被继承,接口部分应略去。
通常比复合的级别低。但是当你面对“并不存在is-a关系”的两个class,其中一个需要访问另一个的protected成员,或需要重新定义其一或多个virtual函数,设计为private继承是合理的。
多重继承(multiple inheritance,MI)-继承一个以上的基类
//多重继承可能带来的歧义问题 class first{ public: void checkout(); }; class sec{ private: virtual void checkout(); }; class thir:public first,public sec{ ... }; thir mp; mp.checkout();//wrong,first和sec都有checkout函数,且只有first的public成员函数checkout是可取用的。但是C++是先找出最佳匹配函数后才检验可取性,first和sec的checkout具有相同的匹配程度,因此造成歧义 mp.first::checkout();//right mp.sec::checkout();//wrong,提示“尝试调用private成员函数”的错误。
避免继承中的名称遮掩
int x;
void func()
{
double x;
std::cin>>x;
}//局部的double x遮掩全局的int x
在类的继承中,derived class作用域被嵌套在base class作用域。
class base{
public:
int x;
private:
virtual void mf1() = 0;//纯虚函数
virtual void mf2();
void mf3();
};
class derived:public base{
public:
virtual void mf1();
void mf4();
};
class base{
public:
int x;
private:
virtual void mf1() = 0;//纯虚函数
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(double);
};
class derived:public base{
public:
virtual void mf1();
void mf3();
void mf4();
};
基于作用域的名称遮掩规则会使得base class内所有名为mf1和mf3的函数都被derived class内的mf1和mf3函数遮掩掉了。
derived d;
int x;
double y;
d.mf1();//right
d.mf1(x);//wrong
d.mf2();//right
d.mf3();//right
d.mf3(y);//wrong
//不论是virtual还是普通函数,只要子类中有同名函数,不管形参是什么,编译器都不会再往父类作用域里面找了。
消除遮掩的方法:
利用using声明式
如果你继承base class并加上重载,而你又希望重新定义或覆写其中一部分,那么你必须为那些原本会被遮掩的每个名称引入一个using声明式,否则某些你希望继承的名称会被遮掩。
class base{ public: int x; private: virtual void mf1() = 0;//纯虚函数 virtual void mf1(int); virtual void mf2(); void mf3(); void mf3(double); }; class derived:public base{ public: using base::mf1; using base::mf3;//使得base class内名为mf1和mf3的所有在derived类作用域内都可见 virtual void mf1(); void mf3(); void mf4(); };
derived d; int x; double y; d.mf1();//right,调用derived::mf1() d.mf1(x);//right,调用base::mf1(int) d.mf2();//right//调用base::mf2() d.mf3();//right//调用derived::mf3() d.mf3(y);//right,调用base::mf3(double)
但using声明式会令继承而来的某给定名称之所有同名函数在derived class中都可见~适合于public继承(派生类继承基类的所有)
利用转交函数(forwarding function)
在private继承时可以实现:派生类只继承基类的部分,比如derived唯一想继承mf1的无参数版本。此时不能使用using声明式,使用转交函数:
class base{...};//同前 class derived:private base{ public: virtual void mf1()//转交函数 { base::mf1(); }//暗自成为inline };
derived d; int x; d.mf1();//right d.mf1(x);//wrong
区分函数的接口继承和函数的实现继承
- 声明一个纯虚函数的目的是为了让derived class只继承函数接口
- 声明非纯虚函数(impure virtual)的目的是让derived class继承该函数的接口和缺省实现
- 声明一个non-virtual函数的目的是为了令derived class继承函数的接口及一份强制性实现
成员函数的接口总是会被继承,下面分别通过代码详细说明。
声明一个纯虚函数的目的是为了让derived class只继承函数接口。但是注意:
纯虚函数必须在派生类中重新声明
也可以为纯虚函数提供定义,例1
--example1 class airplane{ public: virtual void fly()=0; }; void airplane::fly(){//pure virtual函数的实现 ... }
声明非纯虚函数(impure virtual)的目的是让derived class继承该函数的接口和缺省实现。如果derived class对该函数不想做出特殊行为,则可以用base class提供的缺省实现。
class airplane{ public: virtual void fly(); }; void airplane::fly(){ //缺省实现 } class modelA:public airplane{};//如果modelA不想使用缺省实现但是忘记自己对fly函数进行重新定义了呢?
切断接口和缺省实现之间的连接:
class airplane{ public: virtual void fly()=0; protected://protected成员可以被派生类对象访问,不能被用户代码(类外)访问。 void defaultfly();//该函数应该是non-virtual函数,因为任何derived class都不该重新定义该函数。 }; void airplane::defaultfly() { ... }
此时airplane类的接口为fly,缺省实现为defaultfly,若派生类想使用缺省实现可在fly函数中对defaultfly函数做一个inline调用。
class modelA:public airplane{ public: virtual void fly() { defaultfly(); } }; class modelB:public airplane{ public: virtual void fly(); }; void modelB::fly() { ... }
声明一个non-virtual函数的目的是为了令derived class继承函数的接口及一份强制性实现
派生类绝不能重新定义non-virtual函数。
pure virtual函数、impure virtual函数、non-virtual函数之间的差异使你能精确指定想让derived class继承的东西:只继承接口、或是继承接口和一份缺省实现、或是继承接口和一份强制实现。
virtual函数的替换方案(涉及到了Template Method设计模式和Strategy设计模式)
有很多种替换方案~只提一下non-virtual interface(NVI),Template Method设计模式的一种特殊形式。以public non-virtual成员函数(该函数用来控制virtual函数被调用的方法和时间)包裹较低访问性(private或protected)的virtual函数(该函数用来具体实现)。
class base
{
private:
int Base_x;
public:
int process() const
{
int Result = step1();
...
}
private:
virtual int step1() const
{
return Base_x;
}
};
class derived : public base
{
private:
virtual int step1() const//派生类可以重新定义
{
return Base_x*2;
}
};
绝不重新定义继承而来的缺省参数值
因为virtual函数是动态绑定的,但是缺省参数值是静态绑定的。
也就是说,有可能调用一个定义于derived class内的virtual函数的同时,却使用base class为它指定的缺省参数值。例:
class shape{
public:
enum shapecolor{red, green, blue};
virtual void draw(shapecolor color = red) const = 0;//pure virtual,缺省参数值color
};
class rectangle:public shape{
public:
virtual void draw(shapecolor color = green) const;//此处改变了缺省参数值
};
...
//调用:
shape* pc = new rectangle;//动态类型是rectangle,静态类型是shape
pc.draw();//调用rectangle的draw,调用shape的color参数值
因此,需要注意:1、派生类改变(重新定义)缺省参数值。2、基类的缺省参数值的改变,则需要改变派生类。
可以采用其他设计替代virtual函数,例如NVI(non-virtual interface)
class shape {
public:
enum shapecolor{red, green ,blue};
void draw(shapecolor color = red) const//draw函数是non-virtual函数,不能被覆写,因此color的缺省值不会被改变
{
dodraw(color);
}
private:
virtual void dodraw(shapecolor color)const = 0 ;
};
class rectangle :public shape{
public:
..
private:
virtual void dodraw(shapecolor color)const;
};