c++ 多态

一、概念

广义上讲多态可以分为静态多态动态多态。静态多态编译时多态)发生在程序编译阶段主要包括函数重载运算符重载编译时就能确定调用关系。动态多态运行时多态)本章讨论主要动态多态因此狭义多态指的是动态多态多态(polymorphism)按照字面意思多种状态”简单的概括一个接口,多种状态”一个函数接口运行时根据传入参数类型执行不同策略。多态的实现需要三个前提条件

  • 公有继承
  • 函数覆盖(函数重写)override
  • 基类引用/指针指向派生类对象

二、函数覆盖

函数覆盖函数重写)前提虚函数虚函数使用关键字virtual修饰成员函数实现普通虚函数目的实现函数覆盖。虚函数的格式:virtual 返回值类型 函数名 (参数表){}一句话表达之前函数隐藏前提被隐藏基类函数使用virtual修饰函数覆盖。虚函数具有以下性质

  • Qt Creator斜体表示虚函数
  • 虚函数具有传递性基类被覆盖虚函数自动传递派生类覆盖新函数使后者变为虚函数
  • 成员函数析构函数可以设置虚函数静态成员函数构造函数不能设置虚函数
  • 如果函数声明定义分离virtual只需要修饰声明
  • C++11可以派生类覆盖函数后面使用override关键字修饰如果函数覆盖成功不会报错

#include <iostream>

using namespace std;

class Animal

{

public:

virtual void eat()

{

cout << "吃东西" << endl;

}

virtual void speak();

};

void Animal::speak()

{

cout << "ww" << endl;

}

class Dog:public Animal

{

public:

void eat()

{

cout << "吃" << endl;

}

void speak() override

{

cout << "哼" << endl;

}

};

int main()

{

return 0;

}

三、实现

多态往往伴随着函数调用传参基类引用/指针指向派生类对象通常出现在函数传参

nclude <iostream>

using namespace std;

class Animal

{

public:

virtual void eat()

{

cout << "吃东西" << endl;

}

virtual void speak();

};

void Animal::speak()

{

cout << "w'w" << endl;

}

class Dog:public Animal

{

public:

void eat()

{

cout << "吃" << endl;

}

void speak() override

{

cout << "哼" << endl;

}

};

class Cat:public Animal

{

public:

void eat()

{

cout << "吃鱼" << endl;

}

void speak()

{

cout << "害" << endl;

}

};

class Mouse:public Animal

{

public:

void eat()

{

cout << "吃老鼠药" << endl;

}

void speak()

{

cout << "吱吱吱" << endl;

}

};

/**

* @brief test_polymorphysism

* @param a 基类引用,传递栈内存

*/

void test_polymorphysism(Animal& a)

{

a.eat();

a.speak();

}

/**

* @brief test_polymorphysism

* @param a 基类指针,传递堆内存对象

*/

void test_polymorphysism(Animal* a)

{

a->eat();

a->speak();

}

int main()

{

Animal a;

Dog d;

Cat c;

Mouse m;

// 根据传入参数的类型不同,执行不同代码

test_polymorphysism(a);

test_polymorphysism(d);

test_polymorphysism(c);

test_polymorphysism(m);

Animal* a1 = new Animal;

Dog* d1 = new Dog;

Cat* c1 = new Cat;

Mouse* m1 = new Mouse;

// 根据传入参数的类型不同,执行不同代码

test_polymorphysism(a1);

test_polymorphysism(d1);

test_polymorphysism(c1);

test_polymorphysism(m1);

delete a1;

delete d1;

delete c1;

delete m1;

return 0;

}

四、原理 

当使用多态时,上面代码中的Animal类对象会增加一个隐藏的成员指针,指向Animal类的虚函数表,虚函数表与之前的虚基类表类似,也是只有一份,属于Animal类持有,而非某个对象持有。

#include <iostream>

using namespace std;

class Animal

{

public:

virtual void eat()

{

cout << "吃东西" << endl;

}

virtual void speak();

};

void Animal::speak()

{

cout << "fgsjkfghklsj" << endl;

}

class Dog:public Animal

{

public:

void eat()

{

cout << "吃" << endl;

}

virtual void sleep()

{

cout << "呼呼呼" << endl;

}

};

class Cat:public Animal

{

public:

void eat()

{

cout << "吃鱼" << endl;

}

void speak()

{

cout << "害" << endl;

}

};

class Mouse:public Animal

{

public:

void eat()

{

cout << "吃药" << endl;

}

void speak()

{

cout << "吱吱吱" << endl;

}

};

/**

* @brief test_polymorphysism

* @param a 基类引用,传递栈内存

*/

void test_polymorphysism(Animal& a)

{

a.eat();

a.speak();

}

/**

* @brief test_polymorphysism

* @param a 基类指针,传递堆内存对象

*/

void test_polymorphysism(Animal* a)

{

a->eat();

a->speak();

}

int main()

{

Animal a;

Dog d;

Cat c;

Mouse m;

// 根据传入参数的类型不同,执行不同代码

test_polymorphysism(a);

test_polymorphysism(d);

test_polymorphysism(c);

test_polymorphysism(m);

Animal* a1 = new Animal;

Dog* d1 = new Dog;

Cat* c1 = new Cat;

Mouse* m1 = new Mouse;

// 根据传入参数的类型不同,执行不同代码

test_polymorphysism(a1);

test_polymorphysism(d1);

test_polymorphysism(c1);

test_polymorphysism(m1);

cout << sizeof(a) << endl; // 4 虚函数表指针

cout << sizeof(d) << endl; // 4 虚函数表指针,存在优化

// delete a1;

// delete d1;

// delete c1;

// delete m1;

return 0;

}

 

五、隐患 

在使用多态时,可能会出现内存泄漏。

#include <iostream>

using namespace std;

class Animal

{

public:

~Animal()

{

cout << "Animal析构" << endl;

}

};

class Dog:public Animal

{

public:

~Dog()

{

cout << "Dog析构" << endl;

}

};

int main()

{

// 为了示例,强行触发多态

Animal* a = new Dog;

delete a; // 跳过了Dog的析构函数,可能出现内存泄漏

return 0;

}

解决的方法是给基类设置虚析构函数,让析构函数也被虚函数表管理。

如果一个类是基类,建议把析构函数设置虚析构函数,因为编译器自动添加的析构函数没有virtual修饰。

#include <iostream>

using namespace std;

class Animal

{

public:

virtual ~Animal()

{

cout << "Animal析构" << endl;

}

};

class Dog:public Animal

{

public:

~Dog()

{

cout << "Dog析构" << endl;

}

};

int main()

{

// 为了示例,强行触发多态

Animal* a = new Dog;

delete a; // 解决了内存泄漏的问题

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值