第四章 继承

C++是一门可面向对象可面向过程编程的语言,当然它和C语言比起的优势就是面向对象,面向对象是C++的一大特点,那么,上一讲将了面向对象的第一要素,那么这一讲的内容就是面向对象的第二要素——继承。
 

为什么要有继承?
 

如果没有继承,那么类仅仅只是具有一些相关行为的数据结构,所以,这仅仅只是对过程语言的一大改进,而继承的引入,则开辟完全不同的新天地,那么继承能够做些什么?
 

使用继承构建类。
 

使用继承扩展功能。
 

使用继承实现多态(多态的实现不只是继承,还有虚函数,两者缺一不可)。
 

那么什么时候可以使用继承?简单点说有一个原则,就是当我们认为“是一个”的时候就可以考虑继承了,关于“是一个”的概念可能有点模糊,简单点说就是当我们认为某个东西属于某类时就可以使用该原则了,比如猫属于动物,学生也是人,所以这时就可以使用继承啦。在C++里面,有“是一个”的概念,同时也有“有一个”的概念,“有一个”“是一个”这两个概念可能有时候会有些模糊,当我们理解不清的时候可能会套错了模型,“是一个”的概念决定我们使用继承,而且是共有继承,而“有一个”的概念我们不应该使用共有继承,更多的时候我们选择使用复合,当然有时候我们可以使用私有继承,当然在私有继承和复合类型之间怎么决策又有一些技巧性,这里大家可以通过《C++ Effective》一书进行了解。
 

使用继承构建类。
 

如果按照上面我们的“是一个”的原则去写代码可能我们会发现代码没法往下写,因为很多时候“是一个”并不是那么容易被看透,否则也就没有多重继承这种难以理解的语法存在,所以很多时候我们我们使用继承没别的想法,仅仅只是想要复用现有的代码而已。
 

//+————————–
 

class Super{
public:
Super(){ }
virtual ~Super(){ }

void doSomeThing(){ }

};


 

class Sub : public Super{
public:
Sub()

void doOtherThing(){ }

};


 

 

//+—————————
 

我们可以认为Sub就是一个Super,这没毛病,也合情合理,但是当出现多重继承的时候,比如:
 

//+—————————-
 

class Super2{
public:
Super2(){ }
virtual
void ~Super2(){ }


void doSomeOtherThing(){ }

};


 

class Sub : public Super,public Super2{
public:
Sub(){ }

void doOtherThing(){ }

};


 

//+—————————
 

如果我们现在还在认为Sub是一个Super的话就有点难理解啦,但它确确实实具有Super和Super2的功能,他们确确实实也满足“是一个”的原则(否则多态也就没有意义),但是这里我们似乎要澄清一件事,这里并非是因为Sub满足“是一个”才使用继承(当然或许我们设计之初就是这么考虑的),而是因为被继承才被是一个,嗯,好吧,理解起来有些拗口,所以我们才这么认为继承可以用来构建类,是一个或许只是它附加的一个功能。
 

和使用继承构建类比起,使用继承扩展功能似乎更好理解一些,因为它是实实在在的是一个,我们要的也就是这个是一个原则,比如我们手里有一个处理字符串的类——String,而现有的接口都是该String的引用作为参数,而我们想要在新的开发中使用更加便捷的String,但同时有需要使用原有依赖该String的一些接口功能,所以我们不可能重新实现一个String,我们应该做的就是扩展这个String,子类化一个类出来,他提供有我们需要的功能,同时他还是一个String,那些使用String引用作为参数的接口依然不受任何任何影响:
 

//+————————–
 

class MyString : public String{
public:
……
using String::append;

// 加入基类的append只能接受字符串
template<class T>

void append(const T&

val){
std::ostringstream os;


os<<val;


String::append(os.str());



}

……
};


 

 


void testFun(const String&

str){
std::cout<<str<<std::endl;


}
 

 

int main(){
MyString str;


str.append(123);

// 调用子类的append
str.append(“,”);

// 调用基类的append
str.append(128.82);

// 调用子类的append
std::cout<<str<<std::endl;


testFun(str);


return 0;


}
 

 

//+—————————-
 

这就是典型的使用继承去扩展现有功能的例子啦,那么我们下面看看继承的一些高级用法,我们将思路回到前面的点上,我们不再去考虑扩展功能这件事,我们只想如何做好一件事,比如想要编写一个类,他具有比较大小,判断是否相等的操作,那么我们可以如下:
 

//+—————————
class MObj{
public:
MObj(){ }
virtual ~MObj(){ }
friend bool operator<(const MObj&

obj,const MObj&

obj2);


friend bool operator>(const MObj&

obj,const MObj&

obj2);


friend bool operator==(const MObj&

obj,const MObj&

obj2);


friend bool operator!=(const MObj&

obj,const MObj&

obj2);


……
};


 

//+—————————
 

如果我们上面所见,当我们想要实现这些功能,我们需要完成四个函数的编写,如果我们需要再编写一个类也需要这些操作,那么我们又得重新再来一遍,这……如果一个两个还好,要是我们经常这么干一定很不爽,所以这就是我们这里要说的重点,我们只需要完成一部分操作就能够实现全部操作即可,比如我们规定,当我们提供operator<时自动提供operator>,同时提供operator==以及提供operator!=等其他操作。
 

//+—————————
template<class T>
class CmpOrder{
public:
friend bool operator>(const T&

left,const T&

right){

return !(left <right);



}

 

friend bool operator == (const T&

left, const T&

right){

return !(left <right) &

&

!(left >right);



}

 

friend bool operator!=(const T&

left, const T&

right){

return !(left == right);



}

};


 

//+—————————–
 

这是一个通用的比较基类,所以只需要我们的类继承至该类而且实现operator<操作符即可得到这些其他的操作,比如:
 

//+—————————–
 

class MInt : public CmpOrder<MInt>{
private:

int mVal;


public:
MInt(int v) :mVal(v){ }
friend bool operator<(const MInt&

left,const MInt&

right){

return left.mVal <right.mVal;



}

};


 

 

int main(){
MInt val1(7);


MInt val2(8);


std::cout <<(val1 <val2) <<std::endl;


std::cout <<(val1 >val2) <<std::endl;


std::cout <<(val1 == val2) <<std::endl;


std::cout <<(val1 != val2) <<std::endl;


system(“pause”);


return 0;


}
 

//+——————————-
 

当然这种操作手法有一个奇怪的名字,叫奇特的递归模板模式,他的模式:
 

//+——————————-
 

class Sub : public Super<Sub>{…… };


 

//+——————————-
 

他使用模板加继承的手法实现一些强大的功能,当然还有一些更奇怪的继承方法我们后续在实践中慢慢说,比如:
 

 

//+——————————-
 

template<class A,class…Args>
class Sub : public Sub<Args…>{…… }
 

template<>
class Sub{ }
 

//+——————————–
这种继承手法能够实现一些功能强大的组件,比如我们可以以此为基础实现一个数据库。
 

好吧,继承我们就暂时介绍到这里,接下来还有多态等着说呢。

发表评论