第四章 继承

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

void append(const T& val){

std::ostringstream os;

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

}

……

};

void testFun(const String& str){

std::cout

}

int main(){

MyString str;

str.append(123); // 调用子类的append

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

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

std::cout

testFun(str);

return 0;

}

//+----------------------------

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

//+---------------------------

class MObj{

public:

MObj(){}

virtual ~MObj(){}

friend bool operator

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!=等其他操作。

//+---------------------------

template

class CmpOrder{

public:

friend bool operator>(const T& left,const T& right){

return !(left

}

friend bool operator == (const T& left, const T& right){

return !(left right);

}

friend bool operator!=(const T& left, const T& right){

return !(left == right);

}

};

//+-----------------------------

这是一个通用的比较基类,所以只需要我们的类继承至该类而且实现operator

//+-----------------------------

class MInt : public CmpOrder{

private:

int mVal;

public:

MInt(int v) :mVal(v){}

friend bool operator

return left.mVal

}

};

int main(){

MInt val1(7);

MInt val2(8);

std::cout

std::cout val2)

std::cout

std::cout

system("pause");

return 0;

}

//+-------------------------------

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

//+-------------------------------

class Sub : public Super{……};

//+-------------------------------

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

//+-------------------------------

template

class Sub : public Sub{……}

template

class Sub{}

//+--------------------------------

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

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

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180312G1WVGD00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券