• C++多态


    多态

    多态是C++面向对象三大特性之一。
    多态分为两类:

    • 静态多态:函数重载和运算符重载属于静态多态,复用函数名
    • 动态多态:派生类和虚函数实现运行时多态

    静态多态和动态多态区别:

    • 静态多态的函数地址早绑定——编译阶段确定函数地址
    • 动态多态的函数地址晚绑定——运行阶段确定函数地址
    #include
    using namespace std;
    
    class Animal {
    public:
    	//虚函数
    	virtual void speak() {
    		cout << "动物在说话" << endl;
    	}
    };
    
    class Cat : public Animal {
    public:
    	void speak() {
    		cout << "小猫在喵喵" << endl;
    	}
    };
    
    class Dog : public Animal {
    public:
    	void speak() {
    		cout << "小狗在汪汪" << endl;
    	}
    };
    
    //地址早绑定,在编译阶段确定函数地址
    void doSpeak(Animal& animal) {
    	animal.speak();
    }
    
    int main() {
    	Cat cat;
    	doSpeak(cat);
    	Dog dog;
    	doSpeak(dog);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    动态多态满足条件:

    1. 有继承关系
    2. 子类重写父类的虚函数

    重写:函数返回值类型 函数名称 参数列表 完全相同

    动态多态使用:
    父类的指针或引用,指向子类的对象。

    当子类重写父类的虚函数时
    子类中的虚函数表内部会替换成子类的虚函数地址

    多态优点

    • 代码组织结构清晰
    • 可读性强
    • 利于前期和后期的扩展以及维护

    开闭原则:对扩展进行开放,对修改进行关闭。

    纯虚函数和抽象类

    在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容。

    因此可以将虚函数改为纯虚函数。

    纯虚函数语法:

    virtual 返回值类型 函数名(参数列表) = 0;
    
    • 1

    当类中有了纯虚函数,这个类也称为抽象类。
    抽象类无法实例化对象
    子类必须重写抽象类中的纯虚函数,否则也属于抽象类。

    由于传入的对象不同,一个接口有多种形态

    虚析构和纯虚析构

    多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。

    纯虚析构需要声明也需要实现
    有了纯虚析构之后,这个类也属于抽象类,无法实例化对象

    #include
    using namespace std;
    
    class Animal {
    public:
    	virtual void speak() = 0;
    	Animal() {
    		cout << "Animal构造函数调用" << endl;
    	}
    	//利用虚析构可以解决,父类指针释放子类对象时不干净的问题
    	/*virtual ~Animal() {
    		cout << "Animal虚析构函数调用" << endl;
    	}*/
    	virtual ~Animal() = 0;
    };
    
    Animal::~Animal() {
    	cout << "Animal纯虚析构函数调用" << endl;
    }
    
    class Cat : public Animal {
    public:
    	Cat(string name) {
    		cout << "Cat构造函数调用" << endl;
    		m_Name = new string(name);
    	}
    	virtual void speak() {
    		cout << *m_Name << "小猫喵喵" << endl;
    	}
    	~Cat() {
    		if (m_Name != NULL) {
    			delete m_Name;
    			m_Name = NULL;
    			cout << "Cat析构函数调用" << endl;
    		}
    	}
    	string *m_Name;
    };
    
    
    int main() {
    	Animal* animal = new Cat("Tom");
    	animal->speak();
    	//父类指针在析构时候,不会调用子类中析构函数,导致子类如果有堆区属性,会产生内存泄漏
    	delete animal;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    虚析构或纯虚析构都是用来解决通过父类指针释放子类对象的问题。
    如果子类没有堆区数据,可以不写虚析构或纯虚析构。
    拥有纯虚析构函数的类也属于抽象类

  • 相关阅读:
    使用 ErrorStack 在出现报错 ORA-14402 时产生的日志量
    读新乌合之众
    云原生微服务架构图
    tslib库的移植
    3D设计软件Rhinoceros 6 mac 犀牛6中文版功能特征
    关于libxml的使用
    golang的interface转float
    Redis 的底层数据结构和IO模型
    元宇宙的时代来不及解释了快上车
    Servlet
  • 原文地址:https://blog.csdn.net/Caramel_biscuit/article/details/136678365