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{} //+-------------------------------- 这种继承手法能够实现一些功能强大的组件,比如我们可以以此为基础实现一个数据库。 好吧,继承我们就暂时介绍到这里,接下来还有多态等着说呢。 |
|