【基础复习】七:继承与接口
覆盖
- 构造函数从最初始的基类开始构造,各个类的同名变量没有形成覆盖,都是单独的变量
- 就近调用。如果派生类没有定义相关接口,父辈存在相关接口则优先调用父辈接口,如果父辈也不存在相关接口则调用祖父辈接口。
- 派生类的虚函数会覆盖基类的虚函数,因此不管是用什么指针指向内存中的派生类,调用的都是派生类自己的虚函数。
私有继承
- 私有继承使对象不能被派生类的子类访问
- 如果不指定public,c++默认的是私有继承
- 子类只能继承父类的protected和public
- 在无继承的类中,protected和private是没有区别的
- 一个私有的或保护的派生类不是子类,因为非公共的派生类不能做基类能做的所有的事
- 继承和多重继承一般考虑的是公共继承
- 公有继承。在公有继承时,基类的公有成员和保护成员在派生类中保持原来状态。派生类的对象可以访问基类中的公有成员,派生类的成员函数可以访问基类中的公有成员和保护成员。
- 私有继承。在私有继承时,基类的公有成员和保护成员在派生类中变成私有成员。基类的成员只能由直接派生类访问,而无法再往下继承。
- 保护继承。在保护继承时,基类的公有成员和保护成员在派生类中变成保护成员。基类的成员不能被派生类的子类的对象所访问,但可以被派生类的子类访问。
虚函数继承和虚继承
-
当一个类中有虚函数时,每个对象里有虚表指针,指向虚表,虚表里存放了虚函数的地址。虚函数表是顺序存放虚函数地址的,不需要用到链表(link list)。
-
(虚)继承是包含父类的所有内容后,再加上自己定义的内容
-
关键字virtual告诉编译器不应该完成早绑定,而是应当自动安装实现晚绑定所必需的所有机制。当对derived对象通过基类base指针调用函数fun()时,我们将得到恰当的函数
-
利用虚函数,这个对象的合适的函数就能被调用,哪怕编译器还不知道这个对象的特定类型。
-
什么是虚继承?它与一般继承有什么不同?它有什么用?它有什么用?
答:虚拟继承是多重继承中特有的概念,虚拟基类是为解决多重继承而出现的。当一个派生类的两个父类都继承于同一个祖先类时,使父类虚继承于一个祖先类,那么这个派生类的内存中就只有一个祖先类。虚函数继承和虚继承是完全不同的概念,不要在面试中混淆 -
为什么虚函数效率低?
答:因为虚函数需要一次间接的寻址,而一般的函数可以在编译时定位到函数的地址,虚函数(动态类型调用)是要根据某个指针定位到函数的地址。多增加了一个过程,效率肯定会低一些,但带来了运行时的多态。
多重继承
- 多重继承的优点和缺陷
- 大多数系统中类层次往往都有一个公共的基类,这样的结构使用多重继承,稍有不慎会出现菱形继承,这样的继承方式会使得类的访问结构非常复杂。可以用虚继承等其他方法解决。
- 多重继承在面向对象理论中不是必要的,它可以通过单继承和复合结构来取代。
- c++中不定义属性的抽象类就相当于接口interface。
组合
若在逻辑上,A是B的一部分,则不允许B从A派生,而是要用A和其他东西组合出B。
如眼睛、鼻子、嘴巴、耳朵是头的一部分,使用组合代码如下:
#include<iostream>
#include<string>
using namespace std;
class eye
{
public:
void look(){}
};
class nose {
public:
void smell(){}
};
class mouth
{
public:
void eat(){}
};
class ear
{
public:
void listen() {}
};
class head {
public:
void look() {m_eye.look();}
void smell() {m_nose.smell();}
void eat() {m_mouth.eat();}
void listen() {m_ear.listen();}
private:
eye m_eye;
nose m_nose;
mouth m_mouth;
ear m_ear;
};
int main() {
head m_head;
m_head.look();
}
纯虚函数
-
类中有纯虚函数
virtual void Draw()=0
时,这个类是不能实例化成一个对象的 -
C++如何阻止一个类被实例化?
使用抽象类,或者构造函数被声明成private -
构造函数被声明成private会阻止编译器生成默认的拷贝构造函数吗?
如果我们没有定义拷贝构造函数,编译器就会为我们合成一个。与合成默认构造函数不同,即使我们定义了其他构造函数,也会合成拷贝构造函数。
运算符重载和RTTI
from《程序员面试宝典》