之所以把继承和多态放在一起,是因为二者在关系上很难分开。
继承的概念:
在已有类的基础上创建新类。新类包含了原始类的数据成员和方法。
创建新类的主要原因就是添加和替换功能。
继承从两个方面去讨论:
添加:扩展原有类的成员(变量和方法),在继承的基础上扩展
继承的运行方式是单向的,父类与子类有着明确的关系,子类知道与父类的关系,但是父类不知道与子类的关系。
其实这样记不好记,从内存的角度去记比较好记。
从内存的大小上看很好区分。
Father* p_father = new Son; // ok;
Son *p_son = new Father; // error
所以基类指针可以指向父类
Father *p_father;p_father的寻址能力Father类的大小。
Son* p_son;p_son的寻址能力Son类的大小。
Father* p_father = new Son; // ok;
new Son寻址的空间大于p_father的寻址分为,所以ok
Son *p_son = new Father; // error
new Father寻址的空间小于p_father的寻址分为,所以error,出界。
#include <iostream>
using namespace std;
class Father
{
public:
virtual int fun(int a)
{
cout << "call father fun(int a)" << endl;
return 0;
}
};
class Son : public Father
{
public:
virtual int fun(int a)
{
cout << "call son fun(int a)" << endl;
return 0;
}
};
int main(void)
{
Father* father = new Son;
father->fun(10);
return 0;
}
Father* father = new Son;
father->fun(10);
派生类的角度看
我可以访问积累中的protected,和public数据。
禁止继承
c++有一个关键字可以禁止一个类被继承。关键字是final,只需要在类的名字后面加上final关键字。
class A final
{
}
替换:重写原有类的成员函数换(重写):
学好继承区别这两个概念很重要:重载和重写
区别:重载,在同一作用域内,所发生的关系。而重写是类与类之间的关系。且基类有关键字virtual(换句话说就是发生在继承中的现象)
学好继承区别这两个概念很重要:重写和覆盖
区别:如果基类中没有关键字virtual,那他一定是覆盖。有关键字virtual一定是重写。记得重写和覆盖都是对于子类对于父类来说的。
我们知道重载函数与返回值类型无关,那么重写和覆盖也和返回值类型无关。
一下例子区分者四个概念:
#include <iostream>
using namespace std;
class Father
{
public:
void fun()
{
cout << "call father fun()" << endl;
}
virtual int fun(int a)
{
cout << "call father fun(int a)" << endl;
return 0;
}
};
class Son : public Father
{
public:
void fun()
{
cout << "call son fun()" << endl;
}
virtual int fun(int a)
{
cout << "call son fun(int a)" << endl;
return 0;
}
};
int main(void)
{
Father* father = new Son;
father->fun();
father->fun(10);
return 0;
}
我们说过没有virtual关键字且只要函数名相同就会发生将函数名相同的全覆盖。很显然
father->fun()被覆盖。记住是对于子类来说father->fun()被覆盖。
virtual int fun(int a) 很显然基类有关键字virtual所以是重写。
father->fun(); // 输出结果,call father fun(); // 覆盖是对子类来说的。father是分类。
father->fun(10); // 输出结果,call son fun(); // 重写,且要知道一个概念。
重点讲这句:
Father* father = new Son;
father->fun(10);
由于c++是静态语言,而有了virtual虚函数则变为动态的函数,所以,father->fun(10);的输出结果会是call son fun();
father指针指向的内容并没有变,只不过是带有Virtual关键字的函数替换了。这就叫动态编译。
接着上图:
Father里的fun()和Father里的fun(int a) 重载。
Son里的fun()和Son里的fun(int a) 重载。
Father里的函数与Son里的函数不在同一作用域内,所以不存在重载,这是类与类之间的关系,只存在覆盖和重写。
最后一个关键字override
既然基类中有关键字override,就是重写了,那为什么还会有override关键字呢???
override存在的好处之一是,检查此函数是否是真正的重写,如果有关键字override而基类中没有关键字virtual则会报错。
另一个好处,是保证其为真重写,不存在错误。有了override则必须保证重写函数参数列表一模一样,返回值类型可以不一样。
总结起来就是:override就是一个重写函数的保险。
例子:
#include <iostream>
using namespace std;
class Father
{
public:
virtual void fun()
{
cout << "called Father fun()" << endl;
}
virtual void fun(int a)
{
cout << "called Father fun()" << endl;
}
};
class Son:public Father
{
public:
virtual void fun() override
{
cout << "called Son fun()" << endl;
}
virtual void fun(char a) // 没有override
{
cout << "called Son fun()" << endl;
}
};
int main(void)
{
Father* father = new Son;
father->fun();
father->fun(10);
return 0;
}
输出结果:
fun()是真重写,而 virtual void fun(char a) // 没有override,因为参数列表不同,所以irtual void fun(char a) 只是一个新的virtual函数,而不是重写函数。
多态:其实上面已经用到了多态知识只是没总结。
多态:
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
Father* father = new Son;
father->fun();
此方法就是多态。但是要注意,十分注意;实现多态的方式是指针或引用这两种。
Father father;
Son son;
father = son;
father.fun();
father.fun(10);
输出结果:
还是从内存的角度去分析:
father = son;虽然将father强制转换为son但是,father所对应的内存并没有改变,还是那么大,
Father father;
Son son;
cout << &father << endl;
father = son;
cout << &father << endl;