C++类与对象--7 特性三:多态
7.1 多态的实现
(1)静态多态
- 函数重载和运算符重载
- 编译阶段确定函数地址
(2)动态多态
- 派生类(继承关系)和虚函数(派生类重写)实现动态多态
- 运行阶段确定函数地址
- 使用:父类的引用或指针指向子类对象
class Animal
{
public:virtual void speek() // 虚函数{ std::cout << "dongwu speek." << std::endl; }
};
class Cat:public Animal
{
public:void speek() // 对父类虚函数的重写{ std::cout << "cat speek." << std::endl; }
};
class Dog:public Animal
{
public:void speek() // 对父类虚函数的重写{ std::cout << "dog speek." << std::endl; }
};
void doSpesk(Animal & animal) // 父类的引用作为函数形参
{ animal.speek(); }
int main()
{Cat cat;doSpesk(cat); // 父类的引用指向子类的对象Dog dog;doSpesk(dog); // 父类的引用指向子类的对象
return 0;
}
7.2 多态的底层原理
class Animal
{
public:virtual void speek() // 虚函数{ std::cout << "dongwu speek." << std::endl; }
};
- Animal类内部speek函数不加virtual时,Animal类大小为1字节(空对象)
- Animal类内部speek函数加virtual后,Animal类大小为3字节(多了一个虚拟函数指针vfptr)
7.3 纯虚函数和抽象类
- 多态中,父类中实现的虚函数不会被使用,主要使用的是子类中重写的函数
- 将父类中的虚函数写成纯虚函数,父类变成抽象类
- 抽象类特点如下:
- 不能实例化对象
- 子类必须重写纯虚函数,否则子类也自动变成抽象类
class Base
{ virtual void func() = 0; // 纯虚函数语法
};
class Son:public Base
{void func(){}}; // 子类重写纯虚函数
int main()
{Base * son = new Son; // 父类指针指向子类,实现多态son->func();
}
7.4 虚析构和纯虚析构
- 多态中,子类如果开辟了堆区内存,父类指针将无法调用到子类析构函数去释放堆区内存
- 虚析构函数和纯虚析构函数可以解决上述问题
- 虚析构函数和纯虚析构函数除了声明,还需要定义(用于父类堆区数据释放)
- 声明纯虚析构函数的类,变成抽象类
class Base
{
public:virtual ~Base() = 0; // 纯虚析构的声明
};
Base::~Base(){} // 纯虚析构的定义实现(必须有,用于堆区数据释放)
class Son:public Base
{
public:Son(int val){m_val = new int(val);} // 堆区分配内存~Son(){if(m_val != NULL){delete m_val;m_val = NULL;}}int * m_val;
};
int main()
{Base * son = new Son(10); // 父类指针指向子类对象delete son; // 调用子类析构函数释放堆区内存system("pause");return 0;
}