文章目录
一、继承
1、概述
为了减少代码重复内容。
// 父类
class man{
}
// 子类
class d :public man{
}
2、访问控制
public
:不可访问私有属性;protected
:把public
都变成protected
;private
:把public
和protected
变成可访问的。
子类会继承父类的私有属性,但被编译器隐藏了,访问不到私有成员。
3、继承中的构造和析构
子类不会继承父类的
构造
函数和析构
。
- 子类创建对象时,
先调用父类的构造
,在调用自身构造;析构的顺序与之相反;- 若父类中没有合适默认构造,子类可以利用
初始化列表
的方式显示的调用父类的其他构造。
4、同名处理
- 子类和父类中同名不会覆盖。
- 若在子类对象要调用父类的属性时,只需在属性前增加
作用域
即可;
5、多继承
- 同时继承多个基类。
- 会造成二义性,调用时在属性前增加作用域。
6、菱形继承和虚继承
- 两个派生类继承同一个基类而又有某个类同时继承者两个派生类这种继承被称为
菱形继承,或者钻石型继承
。
虚继承:解决菱形方案,操作的是共享的一份数据。
- vbptr 虚基类指针;
- 指向一张虚基类表;
- 通过表找到偏移量;
- 找到共有的变量。
二、复合【composition】
一个类中存在另一个类作为数据成员;
#include<iostream>
using namespace std;
class queue {
public:
private:
deque m_deque;
};
class deque {
public:
private:
//....
};
此时,deque作为queue的数据成员,当queue对象被
创建
时,会先调用deque的构造函数
,在调用自身
的;而析构函数的调用顺序则相反
;
1、继承与复合构造函数调用顺序?
#include<iostream>
using namespace std;
class A {
public:
A() {
cout << "A" << endl;
}
~A() {
cout << "~A" << endl;
}
};
class C {
public:
C() {
cout << "C" << endl;
}
~C() {
cout << "~C" << endl;
}
};
class B : public C{
public:
B() {
cout << "B" << endl;
}
~B() {
cout << "~B" << endl;
}
private:
A a;
};
int main() {
B b;
return 0;
}
三、委托【delegation】
一个类作为另一个类的数据成员,且该成员为指针类型;
此时,queue对象创建,deque不会创建;
四、多态
1、基本概念
多态
是面向对象程序设计语言中数据抽象和继承
之外的第三个基本特征;- 多态性(polymorphism)
提供接口与具体实现之间的另一层隔离
,从而将" what" 和"how"分离开来。多态性改喜了代码的可读性和组织性
,同时也使创建的程序具有可扩展性
;- C++支持编译时多态(静态多态)和运行时多态(动态多态) ,运算符重载和函数重载就是编译时多态,而
派生类
和虚函数
实现运行时多态;- 静态多态和动态多态的区别就是
函数地址是早绑定(静态联编)还是晚绑定(动态联编)
。如果函数的调用,在编译阶段就可以确定函数的调用地址,并产生代码,就是静态多态(编译时多态) ,就是说地址是早绑定的。而如果函数的调用地址不能编译不能在编译期间确定,而需要在运行时才能决定,这这就属于晚绑定(动态多态,运行时多态)。
2、使用
- 静态联编:程序的匹配、连接在编译阶段实现即确定地址,重载函数使用静态联编;
- 动态联编:程序联编推迟到
运行时
进行;
#include<iostream>
using namespace std;
/*
当没有在spreak前加上virtual时函数在编译阶段即确定好了地址;
若要调用猫的,则在该函数前加上virtual。
*/
class Animal
{
public:
virtual void speak(){
cout << "我是动物" << endl;
}
private:
};
class Cat :public Animal
{
public:
void speak(){
cout << "我是小猫" << endl;
}
private:
};
void doSpeak(Animal & animal){
animal.speak();
}
void test(){
Cat cat;
doSpeak(cat);
}
int main(){
test();
system("pause");
return 0;
}
可扩展性:
- 遵循
开闭原则
;对拓展开放,对修改关闭。- 拓展只需继承父类,对其方法进行重写即可。
3、纯虚函数【抽象类】
- 虚函数格式:
virtual void 函数名() = 0;
;- 若父类中有了
纯虚
函数,子类继承父类就必须实现
纯虚函数。- 若父类中有纯虚函数,则这个父类就
无法实例化
对象。
4、虚函数
- 父类中有实现,子类可选择
实现
或者不实现
;- 可能出现在其中有
子类
不需要用到该虚函数
,而有些子类
需要用到且重写该虚函数
时,可将其定义为虚函数且为空函数
;
5、虚析构
- 普通析构函数不会调用子类析构;
- 在多态实现中,在析构前面添加
virtual
,变为虚析构。
纯虚析:
- 写法与纯虚函数一样,在
类内声明,类外实现
;- 父类析构函数必须为virtual 否则出现
undefined behavior
;- 该类也是抽象类,不可实例化。
6、向上向下类型转换
6.1 基类转派生类
- 向下类型转换不安全;
6.2 派生类转基类
- 向上类型转换安全;
若发生多态则总是安全的。