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{}
//+--------------------------------
这种继承手法能够实现一些功能强大的组件,比如我们可以以此为基础实现一个数据库。
好吧,继承我们就暂时介绍到这里,接下来还有多态等着说呢。
领取专属 10元无门槛券
私享最新 技术干货