# 9-C++远征之多态篇-学习笔记

## C++远征之多态篇

1. 普通虚函数 & 虚析构函数
2. 纯虚函数：抽象类 & 接口类
3. RTTI (运行时类型识别)
4. 异常处理
5. 概念区分: 隐藏 & 覆盖 | 早绑定与晚绑定
6. 虚函数表(核心部分)

### c++ 虚函数

• 静态多态（早绑定）
• 动态多态（晚绑定）

#### 静态多态：相同对象收到不同消息

class Rect
{
public:
int calcArea(int width);
int calcArea(int width,int height);//互为重载
}

int main(void)
{
Rect rect;
rect.calcArea(10);
rect.calcArea(10,20);

return 0;
}

#### 动态多态(晚绑定): 不同对象收到相同消息

Shape类

class Shape
{
public:
double calcArea()
{
cout << "calcArea" <<endl;
return 0;
}
}

class Circle:public Shape
{
public:
Circle(double r);
double calcArea();
private:
double m_dR;
}
//Circle计算面积实现
double Circle::calcArea()
{
return 3.14 * m_dR * m_dR;
}
//矩形
class Rect::public Shape
{
public:
Rect(double width,double height);
double calcArea();
private:
double m_dWidth;
double m_dHeight;
}

//矩形计算面积实现
double Rect::calcArea()
{
return m_dWidth * m_dHeight;
}

main函数中的使用：

int main()
{
Shape *shape1 = new Circle(4.0);
Shape *shape2 = new Rect(3.0,5.0);
shape1 -> calcArea();
shape2 -> calcArea();

return 0;
}

### virtual 虚函数

class Shape
{
public:
virtual double calcArea() //虚函数
{
cout << "calcArea" <<endl;
return 0;
}
}

• 子类中不加也可以(系统会自动加上),不过如果考虑子类也有可能成为父类情况下也加上。

### 虚函数代码示例

Shape.h

#ifndef SHAPE_H
#define SHAPE_H

#include <iostream>
using namespace std;

class Shape
{
public:
Shape();
~Shape();
double calcArea();
};

#endif

Shape.cpp

#include "Shape.h"

Shape::Shape()
{
cout << "Shape()" << endl;
}

Shape::~Shape()
{
cout << "~Shape()" << endl;
}

double Shape::calcArea()
{
cout << "Shape - > calcArea()" << endl;
return 0;
}

Circle.h

#ifndef CIRCLE_H
#define CIRCLE_H

#include "Shape.h"
class Circle:public Shape
{
public:
Circle(double r);
~Circle();
double calcArea(); // 同名且参数返回值一致
protected:
double m_dR;
};

#endif

Circle.cpp

#include "Circle.h"

Circle::Circle(double r)
{
cout << "Circle()" << endl;
m_dR = r;
}

Circle::~Circle()
{
cout << "~Circle()" << endl;
}
double Circle::calcArea()
{
cout << "Circle-->calcArea()" << endl;
return 3.14 * m_dR * m_dR;
}

Rect.h

#ifndef RECT_H
#define RECT_H

#include "Shape.h"
class Rect : public Shape
{
public:
Rect(double width,double height);
~Rect();
double calcArea();

protected:
double m_dwidth;
double m_dHeight;
};

#endif // RECT_H

Rect.cpp

#include "Rect.h"

Rect::Rect(double m_dwidth, double m_dHeight)
{
cout << "Rect()" << endl;
this->m_dHeight = m_dHeight;
this->m_dwidth = m_dwidth;
}

Rect::~Rect()
{
cout << "~Rect()" << endl;
}

double Rect::calcArea()
{
cout << "Rect::calcArea()"<< endl;
return m_dwidth * m_dHeight;
}

main.cpp

#include <iostream>
#include "Circle.h"
#include "Rect.h"
#include <stdlib.h>
using namespace std;

int main()
{
// 定义两个父类指针，指向子类对象
Shape *shape1 = new Circle(3.0);
Shape *shape2 = new Rect(3.0, 4.0);

shape1->calcArea();
shape2->calcArea();
//当基类不添加virtual时。打印两遍基类的。

delete shape1;
shape1 = NULL;
delete shape2;
shape2 = NULL;

system("pause");
return 0;
}

Shape.h中的析构函数，与子类重名的函数都加上virtual

class Shape
{
public:
Shape();
virtual ~Shape();
virtual double calcArea();
};

### 虚析构函数解决内存泄露

class Shape
{
public:
Shape();
virtual double calcArea();
} 

class Circle: public Shape
{
public:
Circle(int x,int y,double r);
~Circle();
virtual double calcArea();

private:
double m_dR;
Coordinate *m_pCenter;//圆心坐标
}

Circle::Circle(int x,int y,double r)
{
m_pCenter = new Coordinate(x,y);
m_dR = r;
}
Circle::~Circle()
{
delete m_pCenter;
m_pCenter = NULL;
}

int main(void)
{
Shape *shape1 = new Circle(3,5,4.0)
shape1 -> calcArea();

delete shape1;
shape1 = NULL;

return 0;
}

delete后面跟着父类的指针。只会执行父类的析构函数。那么就无法执行Circle的析构函数，就会造成内存泄露。

virtual -> 析构函数

### virtual在函数中的使用限制

• 普通函数不能是虚函数。 (必须是类的成员函数,不能是全局函数)
• 静态成员函数不能是虚函数。如下面代码。
class Animal
{
public:
virtual static int getCount()//因为被修饰过的静态函数是类的,不属于任何一个对象。
}
• 内联函数不能是虚函数

class Animal
{
public:
inline virtual int eat()
{
}
}
• 构造函数不能修饰虚函数

#### 虚析构函数使用方法代码示例

2-5-VirtualDestructorFunction

Shape.h:

#ifndef SHAPE_H
#define SHAPE_H

#include <iostream>
using namespace std;

class Shape
{
public:
Shape();
virtual ~Shape();
virtual double calcArea();
};

#endif

Shape.cpp

#include "Shape.h"

Shape::Shape()
{
cout << "Shape()" << endl;
}

Shape::~Shape()
{
cout << "~Shape()" << endl;
}

double Shape::calcArea()
{
cout << "Shape - > calcArea()" << endl;
return 0;
}

Rect.h:

#ifndef RECT_H
#define RECT_H

#include "Shape.h"
class Rect : public Shape
{
public:
Rect(double width,double height);
~Rect();
double calcArea();

protected:
double m_dwidth;
double m_dHeight;
};

#endif // RECT_H

Rect.cpp：

#include "Rect.h"

Rect::Rect(double m_dwidth, double m_dHeight)
{
cout << "Rect()" << endl;
this->m_dHeight = m_dHeight;
this->m_dwidth = m_dwidth;
}

Rect::~Rect()
{
cout << "~Rect()" << endl;
}

double Rect::calcArea()
{
cout << "Rect::calcArea()"<< endl;
return m_dwidth * m_dHeight;
}

Circle.h 添加坐标类数据成员指针:

#ifndef CIRCLE_H
#define CIRCLE_H

#include "Shape.h"
#include "Coordinate.h"
class Circle:public Shape
{
public:
Circle(double r);
~Circle();
double calcArea();
protected:
double m_dR;
Coordinate *m_pCenter;
};

#endif

Circle.cpp 实例化坐标对象，析构中释放:

#include "Circle.h"

Circle::Circle(double r)
{
cout << "Circle()" << endl;
m_dR = r;
m_pCenter = new Coordinate(3, 5);
}

Circle::~Circle()
{

cout << "~Circle()" << endl;
delete m_pCenter;
m_pCenter = NULL;
}
double Circle::calcArea()
{
cout << "Circle-->calcArea()" << endl;
return 3.14 * m_dR * m_dR;
}

Coordinate.h

#ifndef COORDINATE_H
#define COORDINATE_H
#include <iostream>

using namespace std;

class Coordinate
{
public:
Coordinate(int x, int y);
~Coordinate();
private:
int m_iX;
int m_iY;
};

#endif

Coordinate.cpp

#include "Coordinate.h"
#include <iostream>
using namespace std;

Coordinate::Coordinate(int x, int y)
{
cout << "Coordinate()" << endl;
m_iX = x;
m_iY = y;
}
Coordinate::~Coordinate()
{
cout << "~Coordinate()" << endl;
}

main.cpp:

#include <iostream>
#include "Circle.h"
#include "Rect.h"
#include <stdlib.h>
using namespace std;

int main()
{
Shape *shape2 = new Rect(3.0, 4.0);
Shape *shape1 = new Circle(3.0);
shape1->calcArea();
shape2->calcArea();

//当基类不添加virtual时。打印两遍基类的。
delete shape1;
shape1 = NULL;
delete shape2;
shape2 = NULL;
system("pause");
return 0;
}

#### virtual限制情况

virtual Shape();
// 报错: “inline”是构造函数的唯一合法存储类
virtual void test() {
// 报错: error C2575: “test”: 只有成员函数和基可以是虚拟的
}
class Shape
{
public:
Shape();
virtual static void test();
//error C2216: “virtual”不能和“static”一起使用
virtual ~Shape();
virtual double calcArea();
};

inline会失效：

virtual inline void test() {
}

#### 练习题

• 虚函数特性可以被继承，当子类中定义的函数与父类中虚函数的声明相同时，该函数也是虚函数。
• 虚函数使用virtual关键字定义，但使用virtual关键字时，并非全部是虚函数(可以是虚继承)
• 虚析构函数是为了避免使用父类指针释放子类对象时造成内存泄露。

### 虚函数与虚析构函数原理

class Shape
{
public:
virtual double calcArea() //虚函数
{
return 0;
}
protected:
int m_iEdge;
}
//子类

class Circle: public Shape
{
public:
Circle(double r);
//Circle使用的也是Shape的虚函数
private:
double m_dR;
}

Circle中并没有定义虚函数，但是他却从父类中继承了虚函数。所以我们在实例化Circle也会产生一个虚函数表指针,它是Circle自己的虚函数表。

class Circle: public Shape
{
public:
Circle(double r);
virtual double calcArea();
private:
double m_dR;
}

Shape没有发生变化。

### 函数的覆盖与隐藏

• 如果没有在子类中定义同名虚函数，那么子类的虚函数表指针会写上父类函数的地址。
• 如果在子类中也定义了同名的虚函数，那么将覆盖父类的函数指针指向子类的函数。

#### 虚析构函数原理。

Shape.h

class Shape
{
public:
virtual double calcArea() //虚函数
{
return 0;
}
virtual ~Shape(){} //虚析构函数
protected:
int m_iEdge;
}

Circle.h

class Circle: public Shape
{
public:
Circle(double r);
virtual double calcArea();
virtual ~Circle(); //不写计算机也会自行定义。

private:
double m_dR;
}

main.cpp:

int main()
{
Shape *shape = new Circle(10.0);

delete shape;
shape = NULL;

return 0;
}

• 比如此时我们删去子类的析构函数。那么将只执行父类的析构函数。

### 证明虚函数表指针的存在(代码示例)

• 对象的大小: 在类实例化的对象当中数据成员所占据的大小,不包括成员函数。
• Shape没有数据成员（理论上不占内存); Circle有一个int型的数据成员，应该占四个。
• 对象的地址：通过类实例化的对象，它占据的内存单元的首地址
• 对象成员的地址：当用一个类实例化一个对象之后，这个对象中可能与一个或多个数据成员，每一个数据成员所占据的地址就是这个对象的成员地址；对象的数据成员由于数据类型不同，占据的内存大小也不同，地址也是不同的。
• 虚函数表指针：在具有虚函数的情况下实例化对象时，这个对象的第一个内存存储的是一个指针，即虚函数表的指针，占四个内存单元，因此我们可以通过计算对象的大小来证明指针的存在。

2-8-VirtualTablePointer

Shape.h

#ifndef SHAPE_H
#define SHAPE_H

#include <iostream>
using namespace std;

class Shape
{
public:
Shape();
~Shape();
double calcArea();
//virtual ~Shape();
//virtual double calcArea();
};

#endif

Shape.cpp

#include "Shape.h"

Shape::Shape()
{
//cout << "Shape()" << endl;
}

Shape::~Shape()
{
//cout << "~Shape()" << endl;
}

double Shape::calcArea()
{
cout << "Shape - > calcArea()" << endl;
return 0;
}

Circle.h

#ifndef CIRCLE_H
#define CIRCLE_H

#include "Shape.h"
class Circle:public Shape
{
public:
Circle(int r);
~Circle();
protected:
int  m_iR;
};

#endif

Circle.cpp

#include "Circle.h"

Circle::Circle(int r)
{
m_iR = r;
}
Circle::~Circle()
{
}

main.cpp:

#include <iostream>
#include "Circle.h"
#include <stdlib.h>
using namespace std;

int main()
{
Shape shape;
cout << sizeof(shape) << endl;
// Shape对象没有任何的数据成员。理论应该为0.
Circle circle(100);
cout << sizeof(circle) << endl;
// Circle 有一个int数据成员 理论为4.

system("pause");
return 0;
}

4是因为int占四个字节。如何解释1?

• 在没有一个数据成员的情况下，C++会给该对象1个内存单元来标记此对象的存在，
• 而对于含有内存单元的对象来说，他的大小则是该数据成员的大小

main.cpp:

#include <iostream>
#include "Circle.h"
#include <stdlib.h>
using namespace std;

int main()
{
Shape shape;
int *p = (int *)&shape;
// 强制类型转换
cout << p << endl;

Circle circle(100);
int *q = (int *)&circle;
cout << q << endl;

// Shape和Circle对象在内存中地址不同。
cout << (unsigned int)(*q) << endl;
// 打印出里面数据成员的值
system("pause");
return 0;
}

### 虚函数示例2

2-9-VirtualFunction2

Shape.h (其中calcArea被加上了virtual关键字)

class Shape
{
public:
Shape();
~Shape();

// virtual ~Shape();
virtual double calcArea();
};

main.cpp

int main()
{
Shape shape;
cout << sizeof(shape) << endl;

Circle circle(100);
cout << sizeof(circle) << endl;

//打印出里面存着的值
system("pause");
return 0;
}

• 指向Shape的指针会指向一个虚拟函数表。因为是个指针，所以占四个内存空间。

Circle因为继承自Shape也会拥有一个虚函数表，加上自己原本的数据成员，对象大小为8。

Shape.h改为如下:

class Shape
{
public:
Shape();
double calcArea();
virtual ~Shape(); // 虚析构函数也有虚函数表。
};

int main()
{
Shape shape;
int *p = (int *)&shape;
cout << (unsigned int)(*p) << endl;
// 虚函数表地址

Circle circle(100);
int *q = (int *)&circle;
cout << (unsigned int)(*q) << endl;
// 打印出的还是虚函数表地址
system("pause");
return 0;
}

Circle中前四个内存单元是虚函数表指针地址。后四个是数据成员100。

int *q = (int *)&circle;
q++;
cout << (unsigned int)(*q) << endl;

• Circle指针8位。前四位是指针指向虚地址表首地址。后四位是里面存着的内容。
• 父类和子类拥有不同的属于自己的虚函数表。

• 在C++中多态的实现是通过虚函数表实现的
• 每个类只有一份虚函数表，所有该类的对象共用同一张虚函数表
• 两张虚函数表中的函数指针可能指向同一个函数。(当子类没有父类的同名函数)

### 巩固练习：

#include <iostream>
#include <stdlib.h>
#include <string>
using namespace std;

/**
*  定义动物类：Animal
*  成员函数：eat()、move()
*/
class Animal
{
public:
// 构造函数
Animal(){cout << "Animal" << endl;}
// 析构函数
virtual  ~Animal(){cout << "~Animal" << endl;}
// 成员函数eat()
virtual void eat(){cout << "Animal -- eat" << endl;}
// 成员函数move()
virtual void move(){cout << "Animal -- move" << endl;}
};

/**
* 定义狗类：Dog
* 此类公有继承动物类
* 成员函数：父类中的成员函数
*/
class Dog : public Animal
{
public:
// 构造函数
Dog(){cout << "Dog" << endl;}
// 析构函数
virtual ~Dog(){cout << "~Dog" << endl;}
// 成员函数eat()
virtual void eat(){cout << "Dog -- eat" << endl;}
// 成员函数move()
virtual void move(){cout << "Dog -- move" << endl;}
};

int main(void)
{
// 通过父类对象实例化狗类
Animal *a = new Dog();
// 调用成员函数
a ->eat();
a ->move();
// 释放内存
delete a;
a = NULL;

return 0;
}

• 因为父类使用虚析构函数。所以释放内存。
• 因为子类中有父类的同名函数，所以在子类的虚函数表中原本指向父类方法的指针被覆盖为子类自己的函数指针。
• 实现了父类对象调用子类方法。

### 纯虚函数 & 抽象类

class Shape
{
public:
virtual double calcArea()//虚函数
{return 0;}
virtual double calcPerimeter() = 0;//纯虚函数
}

• 没有函数体
• 直接等于0

• 在虚函数表中直接写为0，

• 包含纯虚函数的类，就是抽象类。上面含有纯虚函数的shape类就是一个抽象类。

• 纯虚函数无法调用，所以抽象类无法实例化对象

class Person
{
public:
Person(string name);
virtual void work() =0;
virtual void printInfo() =0;
};
class Worker: public Person
{
public:
Worker(string name)
virtual void work() = 0;
virtual void printInfo() { cout << m_strName <<endl;}
private:
string m_strName;
};
class Dustman: public Worker
{
public:
Worker(string name)
virtual void work() {cout << "扫地"};
virtual void printInfo() { cout << m_strName <<endl;}
private:
string m_strName;
};
• 抽象类的子类也有可能是抽象类。抽象类的子类只有把抽象类当中的所有纯虚函数都做了实现，子类才可以实例化对象。
• 上面代码中work只把子类的两个实现了一个。只有dustman
才能实例化对象。

### 抽象类代码示例

3-2-AbstractClass

Person.h

#ifndef PERSON_H//假如没有定义
#define PERSON_H//定义

#include <string>
using namespace std;

class Person
{
public:
Person(string name);
virtual ~Person() {};
virtual void work() =0; // 纯虚函数
private:
string m_strName;
};

#endif //结束符

Person.cpp

#include "Person.h"

Person::Person(string name)
{
m_strName = name;
// 不实现纯虚函数
}

Worker.h

#include <string>
using namespace std;
#include "Person.h"
class Worker:public Person
{
public:
Worker(string name,int  age);
//virtual void work();
virtual ~Worker() {};
private:
int  m_iAge;
};

Worker.cpp

#include "Worker.h"
#include <iostream>
using namespace std;
Worker::Worker(string name,int age):Person(name)
{
m_iAge = age;
}

//void Worker::work()
//{
//  cout << "work()" << endl;
//}

Dustman.h

#ifndef DUSTMAN_H
#define DUSTMAN_H

#include "Worker.h"
class Dustman :public Worker
{
public:
Dustman(string name, int age);
virtual void work();
};

#endif

Dustman.cpp

#include "Dustman.h"
#include <iostream>
using namespace std;

Dustman::Dustman(string name, int age) :Worker(name, age)
{

}
void Dustman::work() {
cout << "扫地" << endl;
}

main.cpp

#include <iostream>
#include "Person.h"
#include "Worker.h"
#include <stdlib.h>
#include "Dustman.h"
int main()
{
//Person person("张三"); // 报错:“Person”: 不能实例化抽象类
//Worker worker("zhangsan", 17); // 报错:“Worker”: 不能实例化抽象类
Dustman dustman("zhangsan", 20);

system("pause");
return 0;
}

// virtual 函数返回类型 函数名（）=0;
// 纯虚函数里面不用写任何代码
virtual void work() =0; // 纯虚函数

#### 练习

• 只有函数声明没有函数定义,直接等于0的虚函数是纯虚函数。
• 含有纯虚函数的类叫做抽象类。
• 不可以使用含有纯虚函数的类实例化对象。
• 抽象类的子类也可以是抽象类。

#### 单元练习

#include <iostream>
#include <stdlib.h>
#include <string>
using namespace std;

/**
* 定义动物类：Animal
* 虚函数：eat()
* 纯虚函数：move()
* 数据成员：m_strName
*/
class Animal
{
public:
// 默认构造函数
Animal(){};
// 含参构造函数
Animal(string name){m_strName = name; cout << "Animal" << endl;}
// 虚析构函数
virtual ~Animal(){cout << "~Animal" << endl;}
// 虚成员函数
virtual void eat(){cout << "Animal--" << m_strName << "-- eat" << endl;}
// 纯虚函数
virtual void move() = 0;
public:
// 数据成员
string m_strName;
};

/**
* 定义狗类：Dog
* 公有继承动物类
* 虚成员函数：eat()、move()
*/
class Dog: public Animal
{
public:
// 默认构造函数
Dog(){};
// 含参构造函数
Dog(string name){m_strName = name; cout << "Dog" << endl;}
// 虚析构函数
virtual ~Dog(){cout << "~Dog" << endl;}
// 虚成员函数eat()
virtual void eat(){cout << "Dog--" << m_strName << " -- eat" << endl;}
// 虚成员函数move()
virtual void move(){cout << "Dog--" << m_strName << " -- move" << endl;}
public:
// 数据成员
string m_strName;
};

int main(void)
{
// 通过动物类实例化狗类
Animal *p = new Dog("狗类");
// 调用成员函数
p ->eat();
p ->move();
// 释放内存
delete p;
p = NULL;

return 0;
}

### 接口类

• 含有纯虚函数的类叫抽象类；仅含有纯虚函数的类叫接口类
• 接口类没有数据成员，只有成员函数，仅有的成员函数还全部都是纯虚函数
class Shape
{
public:
virtual double calcArea() = 0;//计算面积
virtual double calcPerimeter() = 0;//计算周长
}

class Flyable
{
public:
virtual void takeoff() = 0;//起飞
virtual void land() = 0; //降落
}

class Bird:public Flyable
{
public:
virtual void takeoff(){}
virtual void land(){}
private:
//...
}

class flyMatch(Flyable *a,Flyable *b)
{
//...
a->takeoff();
b->takeoff();
a->land;
b->land;
}

class CanShot
{
public:
virtual void aim() = 0;//瞄准
}

class Plane:public Flyable,public CanShot
{
virtual void takeoff(){}
virtual void land(){}
virtual void aim(){}
}
//传入两个plane，plane is a canshot
void fight(CanShot *a,CanShot *b)
{
a -> aim();
b -> aim();
}

class Plane:public Flyable
{
//...
virtual void takeoff(){}
virtual void land(){}
}

// 要实例化飞机，就必须实现起飞和降落

class FighterJet:public Plane,public CanShot
{
virtual void aim(){}
}

void airBattle(FighterJet *a,FighterJet *b)
{
//调用flyable中约定的函数
//调用canshot中约定的函数
}

### 接口类代码示例

3-6-InterfaceClass

Flyable.h

#ifndef FLYABLE_H
#define FLYABLE_H

class Flyable
{
public:
virtual void takeoff() = 0;//起飞
virtual void land() = 0; //降落
};

#endif

Plane.h

#ifndef PLANE_H
#define PLANE_H
#include "Flyable.h"
#include <string>
using namespace std;
class Plane :public Flyable
{
public:
Plane(string code);
virtual void takeoff();
virtual void land();
void printCode();
private:
string m_strCode;
};
#endif

Plane.cpp

#include "Plane.h"
#include <iostream>
using namespace std;
Plane::Plane(string code)
{
m_strCode = code;
}
void Plane::takeoff()
{
cout << "plane - takeoff" << endl;

}
void Plane::land()
{
cout << "plane - land" << endl;

}
void Plane::printCode()
{
cout << m_strCode << endl;
}

FighterPlane.h

#ifndef FIGHTERPLANE_H
#define FIGHTERPLANE_H

#include "Plane.h"
class FighterPlane:public Plane
{
public:
FighterPlane(string code);
virtual void takeoff();
//因为plane已经实现过了，所以它可实现也可也不
virtual void land();
};
#endif 

FighterPlane.cpp

#include <iostream>
#include "FighterPlane.h"
using namespace std;

FighterPlane::FighterPlane(string code) :Plane(code)
{
}
void FighterPlane::takeoff()
{
cout << "FighterPlane -- takeoff" <<endl;
}
void FighterPlane::land()
{
cout << "FighterPlane -- land" << endl;
}

main.cpp:

#include <iostream>
using namespace std;
#include <stdlib.h>
#include "FighterPlane.h"

void flyMatch(Flyable *f1,Flyable *f2)
{
f1->takeoff();
f1->land();
f2->takeoff();
f2->land();
}
int main(void)
{
Plane p1("001");
Plane p2("002");
p1.printCode();
p2.printCode();

flyMatch(&p1,&p2);

system("pause");
return 0;
}

int main(void)
{
FighterPlane p1("001");
FighterPlane p2("002");
p1.printCode();
p2.printCode();

flyMatch(&p1,&p2);

system("pause");
return 0;
}

class FighterPlane :public Plane,public Flyable
{};
class Plane //并把飞机中的纯虚函数 声明 & 定义 去掉

#include <iostream>
using namespace std;
#include <stdlib.h>
#include "FighterPlane.h"

void flyMatch(Plane *f1,Plane *f2)
{
f1->printCode();
f2->printCode();
}

int main(void)
{
FighterPlane p1("001");
FighterPlane p2("002");

flyMatch(&p1,&p2);

system("pause");
return 0;
}

• 接口类中仅有纯虚函数，不能含有其他函数，也不能含有数据成员。
• 可以使用接口类指针指向其子类对象，并调用子类对象中实现的接口类中的纯虚函数。
• 一个类可以继承一个接口类，也可以继承多个接口类。
• 一个类继承接口类的同时也可以继承非接口类。

### 巩固练习

#include <iostream>
#include <stdlib.h>
#include <string>
using namespace std;

/**
* 定义射击类：CanShut
*/
class CanShut
{
public:
virtual void aim() =0;
};

/**
* 定义枪类：Gun
* 公有继承射击类
*/
class Gun : public CanShut
{
public:
virtual void aim()
{
cout << "Gun -- aim" << endl;
}
{
cout << "Gun -- reload" << endl;
}
};

/**
* 定义含参函数射击：hunting
*/
void hunting(CanShut *s)
{
s->aim();
}

int main(void)
{
// 实例化枪对象
CanShut *p = new Gun();
// 调用含参函数hunting,将对象枪传入函数中
hunting(p);//因为已经是个指针。所以直接传入指针本身。
//如果是对象。那要加上&取地址符
// 释放内存
delete p;
p = NULL;

return 0;
}

### RTTI (运行时类型识别)

Run-Time Type Identification

class Flyable
{
public:
virtual void takeoff() = 0;//起飞
virtual void land() = 0; //降落
};

class Bird:public Flyable
{
public:
void foraging(){} // 觅食
virtual void takeoff(){}
virtual void land(){}
private:
//...
};

class Plane:public Flyable
{
public:
void carry(){} // 运输
virtual void takeoff(){}
virtual void land(){}
};

void doSomething(Flyable *obj)
{
obj ->takeoff();
//如果是bird，则觅食
//如果是plane，则运输
obj -> land();
}

void doSomething(Flyable *obj)
{
obj ->takeoff();
cout << typeid(*obj).name() <<endl; // 打印出对象类型
if(typeid(*obj) == typeid(Bird))
{
Bird *bird = dynamic_cast<Bird *>(obj); // 尖括号里填写目标类型
//尖括号内是我们想要转化成的类型。
bird -> foraging();
}
if(typeid(*obj) == typeid(Plane))
{
Plane *plane = dynamic_cast<Plane *>(obj); // 尖括号里填写目标类型
//尖括号内是我们想要转化成的类型。
plane -> carry();
}
obj -> land();
}

dynamic_cast注意事项：

• 只能应用于指针和引用的转换
• 要转换的类型中必须包含虚函数
• 转换成功返回子类的地址，失败返回NULL

typeid注意事项：

• type_id返回一个type_info对象的引用
• 如果想通过基类的指针获得派生类的数据类型,基类必须带有虚函数
• 只能获取对象的实际类型。(不能判断当前指针是基类还是子类)

name() & 运算符重载等号，使得我们可以直接用==进行比对

#### RTTI代码示例

4-2-RTTICode

Flyable.h

#ifndef FLYABLE_H
#define FLYABLE_H

class Flyable
{
public:
virtual void takeoff() = 0;//起飞
virtual void land() = 0; //降落
};

#endif

Plane.h

#ifndef PLANE_H
#define PLANE_H

#include <string>
#include "Flyable.h"
using namespace std;

class Plane :public Flyable
{
public:
void carry();
virtual void takeoff();
virtual void land();
};

#endif

Plane.cpp

#include <iostream>
#include "Plane.h"
using namespace std;

void Plane::carry()
{
cout << "Plane::carry()" << endl;
}
void Plane::takeoff()
{
cout << "Plane::takeoff()" << endl;
}
void Plane::land()
{
cout << "Plane::land()" << endl;
}

Bird.h

#ifndef BIRD_H
#define BIRD_H

#include "Flyable.h"
#include <string>
using namespace std;
class Bird :public Flyable
{
public:
void foraging();
virtual void takeoff();
virtual void land();
};

#endif // !BIRD_H

Bird.cpp

#include <iostream>
#include "Bird.h"
using namespace std;

void Bird::foraging()
{
cout << "Bird::foraging()" << endl;
}
void Bird::takeoff()
{
cout << " Bird::takeoff()" << endl;
}
void Bird::land()
{
cout << "  Bird::land()" << endl;
}

main.cpp:

#include <iostream>
#include "Bird.h"
#include "Plane.h"
using namespace std;
#include <stdlib.h>

void doSomething(Flyable *obj)
{
cout << typeid(*obj).name() << endl;
obj->takeoff();
if (typeid(*obj) == typeid(Bird))
{
Bird *bird = dynamic_cast<Bird *>(obj);
bird->foraging();
}
if (typeid(*obj) == typeid(Plane))
{
Plane *plane = dynamic_cast<Plane *>(obj);
plane->carry();
}

obj->land();
}

int main()
{
Bird b;
doSomething(&b);

system("pause");
return 0;
}

• 传入的是bird,所以执行了bird分支下的觅食。
• 当传入是plane时,则会执行carry。
int main()
{
Plane p;
doSomething(&p);

system("pause");
return 0;
}

#### 代码说明typeid, dynamic_cast的注意事项

int main()
{
int i =0;
cout << typeid(i).name() << endl;
}

int main()
{
Flyable *p = new Bird();
cout << typeid(p).name() << endl;
cout << typeid(*p).name() << endl;
system("pause");
return 0;
}

#ifndef FLYABLE_H
#define FLYABLE_H
class Flyable
{
public:
void takeoff(){}//起飞
void land() {}//降落
};
#endif

#ifndef BIRD_H
#define BIRD_H
#include "Flyable.h"
#include <string>
using namespace std;
class Bird :public Flyable
{
public:
void foraging();
void takeoff();
void land();
};

#endif // !BIRD_H

Bird和flyable此时变成普通的继承。

 int main()
{
Flyable *p = new Bird();
Bird *b = dynamic_cast<Bird *>p;
// 会报错: “dynamic_cast”:“Flyable”不是多态类型

system("pause");
return 0;
}

int main()
{
Flyable p;
Bird b = dynamic_cast<Bird>p;
//“dynamic_cast”:“Flyable”不是多态类型

system("pause");
return 0;

#### 练习题

• 继承关系不是RTTI的充分条件，只是必要条件，所以存在继承关系的类不一定可以用RTTI技术
• RTTI的含义是运行时类型识别
• RTTI技术可以通过父类指针识别其所指向对象的真实数据类型
• 运行时类型别必须建立在虚函数的基础上，否则无需RTTI技术

### 巩固练习

#include <iostream>
#include <stdlib.h>
#include <string>
#include <typeinfo>
using namespace std;

/**
* 定义移动类：Movable
* 纯虚函数：move
*/
class Movable
{
public:
virtual void move() = 0;
};

/**
* 定义公交车类：Bus
* 公有继承移动类
* 特有方法carry
*/
class Bus : public Movable
{
public:
virtual void move()
{
cout << "Bus -- move" << endl;
}

void carry()
{
cout << "Bus -- carry" << endl;
}
};

/**
* 定义坦克类：Tank
* 公有继承移动类
* 特有方法fire
*/
class Tank :public Movable
{
public:
virtual void move()
{
cout << "Tank -- move" << endl;
}

void fire()
{
cout << "Tank -- fire" << endl;
}
};

/**
* 定义函数doSomething含参数
* 使用dynamic_cast转换类型
*/
void doSomething(Movable *obj)
{
obj->move();

if(typeid(*obj) == typeid(Bus))
{
Bus *bus = dynamic_cast<Bus *>(obj);
bus->carry();
}

if(typeid(*obj) == typeid(Tank))
{
Tank *tank = dynamic_cast<Tank *>(obj);
tank->fire();
}
}

int main(void)
{
Bus b;
Tank t;
doSomething(&b);
doSomething(&t);
return 0;
}

### 异常处理

• try...catch...

throw抛出异常

void fun1()
{
throw 1; // 抛出数字1
}

int main(){
try {
fun1(); // 如果正常运行，catch里的不会被执行
}catch(int) //throw的是1,所以用int类型捕获
{
//.....
}
return 0;
}
try{
fun1();
}
catch(int)
{}
catch(double)
{}
catch(...) //括号里三个点,捕获所有的异常
{}

• 抛出值 & 捕获数据类型。

char getChar(const string& aStr,const int aIndex)
{
if (aIndex > aStr.size())
{
throw string("ivalid index!");
}
return aStr[aIndex]; //根据字符串和下标拿到对应下标位置的字符
}
string str("hello world");
char ch;
try{
ch = getChar(str,100); //这句抛异常，下句不会运行
cout << ch << endl;
}catch(string& aval){
cout << aval << endl;
}

• 数组下标越界
• 除数为0
• 内存不足

void fun1()
{
throw new SizeErr();
}

void fun2()
{
throw new MemoryErr();
}

try{
fun1();
}catch(Exception &e)
{
e.xxx();
}

try{
fun2();
}catch(Exception &e)
{
e.xxx();
}

#### 异常处理代码示例

5-2-ErrorDeal

Exception.h

#ifndef EXCEPTION_H
#define EXCEPTION_H
class Exception
{
public:
virtual void printException();
virtual ~Exception() {}
};
#endif

Exception.cpp

#include "Exception.h"
#include <iostream>
using namespace std;

void Exception::printException()
{
cout << " Exception::printException()" << endl;
}

IndexException.h

#ifndef INDEX_EXCEPTION_H
#define INDEX_EXCEPTION_H

#include "Exception.h"
class IndexException:public Exception
{
public:
virtual void printException();
};
#endif

IndexException.cpp

#include "IndexException.h"
#include <iostream>
using namespace std;

void IndexException::printException()
{
cout << "提示：下标越界" << endl;
}

main.cpp

#include <iostream>
#include <stdlib.h>
#include "IndexException.h"
using namespace std;
void test()
{
throw 0.1;
}
int main(void)
{
try
{
test();
}
catch (double)
{
cout << "exception" << endl;
}
system("pause");
return 0;
}

throw 1.0, double类型捕获。

catch (double &e)
{
cout << e << endl;
}

main.cpp

#include <iostream>
#include <stdlib.h>
#include "IndexException.h"
using namespace std;
void test()
{
throw IndexException();
}
int main(void)
{
try
{
test();
}
catch (IndexException &e)
{
e.printException();
}
system("pause");
return 0;
}

int main(void)
{
try
{
test();
}
catch (Exception &e)
{
e.printException();
}
system("pause");
return 0;
}

int main(void)
{
try
{
test();
}
catch (...)
{
cout << "error" << endl;
}
system("pause");
return 0;
}

### 练习题

• 在C++中异常处理通常使用try...catch...语法结构。
• 一个try语句可以对应一个或多个catch语句，但不能没有catch语句
• C++中使用throw抛出异常，通过catch捕获异常

#### 巩固练习

#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;

/**
* 定义函数division
* 参数整型dividend、整型divisor
*/
int division(int dividend, int divisor)
{
if(0 == divisor)
{
// 抛出异常，字符串“除数不能为0”
throw string("除数不能为0");
}
else
{
return dividend / divisor;
}
}

int main(void)
{
int d1 = 0;
int d2 = 0;
int r = 0;
cin >> d1;
cin >> d2;
// 使用try...catch...捕获异常
try{
r = division(d1,d2);
cout << r << endl;
}catch(string &str){
cout << str <<endl;
}

return 0;
}

07-22 61