分享

第四章 继承

 cntagu 2018-03-11

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;

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<(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 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{

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{……};


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


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



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


template

class Sub : public Sub{……}


template<>

class Sub{}


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

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


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


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多