
 
文章目录
- 1.多态的介绍
 - 1.1概念
 - 1.2分类
 
- 2.多态的实现
 - 2.1虚函数的介绍
 - 1.定义
 - 2.重写
 - 1.通常情况
 - 2.特例
 
- 2.2多态构成条件
 - 2.3破坏多态的情况
 - 1.父类没有虚函数 子类有对应虚函数重写
 - 2.父类有虚函数 子类没有重写对应虚函数
 
- 2.4虚析构和纯虚析构
 - 1.虚析构
 - 2.纯虚析构
 - 3.虚析构/纯虚析构存在的意义
 
- 2.5总结及拓展
 - 1.final:修饰父类虚函数,表示该虚函数不能再被重写
 - 2.override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
 - 3.重载、覆盖(重写)、隐藏(重定义)
 
- 2.6抽象类
 - 1.概念
 - 2.接口继承和实现继承
 - 3.代码演示
 
1.多态的介绍
1.1概念

 简单来说,同样一种行为,不同对象完成时会呈现不同的状态。比如:
- 吃东西 牛吃草 狼吃羊 猫吃鱼
 - 买票 学生票 军人票 普通票
 
1.2分类
静态多态: 函数重载 和 运算符重载 — 编译阶段确定函数地址
 动态多态: 虚函数 — 运行阶段确定函数地址
2.多态的实现
2.1虚函数的介绍
1.定义
被virtual修饰的类的成员函数
 
2.重写
1.通常情况
派生类重写基类的虚函数:派生类中有一个跟基类完全相同的虚函数(返回值类型、函数名字、参数列表完全相同)
2.特例
- 子类重写父类的虚函数【不加virtual也构成重写–继承基类的虚函数后实现】
 - 协变:返回值是父子关系的指针或引用–返回值不同也构成重写
 
对照组:
class Animal
{
public:
   virtual void express()
   {
        cout << "我在疯狂动物叫" << endl;
   }
};
class Dog :public Animal
{
public:
    virtual void express()
    {
        cout << "我在疯狂狗叫" << endl;
    }
};
void func(Animal& animal)
{
    animal.express();
}
int main() 
{
    Animal animal;
    func(animal);
    Dog dog;
    func(dog);
    return 0;
}
 
实验组1:
class Animal
{
public:
    virtual Animal* express()
    {
        cout << "我在疯狂动物叫" << endl;
        return this;
    }
};
class Dog :public Animal
{
public:
    virtual Dog* express()
    {
        cout << "我在疯狂狗叫" << endl;
        return this;
    }
};
void func(Animal& animal)
{
    animal.express();
}
int main()
{
    Animal animal;
    func(animal);
    Dog dog;
    func(dog);
    return 0;
}
 
实验组2:
class Ox  //牛
{
};
class Bull :public Ox//公牛
{
};
class Animal
{
public:
    virtual Ox* express()
    {
        cout << "我在疯狂动物叫" << endl;
        return nullptr;
    }
};
class Dog :public Animal
{
public:
    virtual Bull* express()
    {
        cout << "我在疯狂狗叫" << endl;
        return nullptr;
    }
};
void func(Animal& animal)
{
    animal.express();
}
int main()
{
    Animal animal;
    func(animal);
    Dog dog;
    func(dog);
    return 0;
}
 
2.2多态构成条件
- 指向子类对象的基类指针或引用调用虚函数
 - 派生类重写基类的虚函数
 
class Animal
{
public:
   virtual void express()
   {
        cout << "我在疯狂动物叫" << endl;
   }
};
class Dog :public Animal
{
public:
    virtual void express()
    {
        cout << "我在疯狂狗叫" << endl;
    }
};
void func(Animal& animal)
{
    animal.express();
}
int main() 
{
    Animal animal;
    func(animal);
    Dog dog;
    func(dog);
    return 0;
}
 

例题

class A
{
public:
	virtual void func(int value = 1) 
	{ 
		cout << "A->" << value << endl; 
	}
	virtual void test() 
	{ 
		func();
	}
};
class B : public A
{
public:
	void func(int value = 0)
	{ 
		cout << "B->" << value << endl; 
	}
};
int main()
{
	A* pb = new B;  //B -> 1
	
	pb->test();
	return 0;
}
 
2.3破坏多态的情况
1.父类没有虚函数 子类有对应虚函数重写
class Dad
{
public:
	void Cook()
	{
		cout << "佛跳墙" << endl;
	}
	virtual void Work()
	{
		cout << "Work" << endl;
	}
	int _a = 0;
};
class Son : public Dad
{
public:
	virtual void Cook()
	{
		cout << "方便面" << endl;
	}
	int _b = 0;
};
void Test(Dad& p)
{
	p.Cook();
}
int main()
{
	Dad dad;
	Test(dad);
	Son son;
	Test(son);
	return 0;
}
 
父类没有虚函数表 编译时就已经确定了函数地址 即不论父类或子类对象调用的都是父类的成员函数
2.父类有虚函数 子类没有重写对应虚函数
#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
using namespace std;
class Dad
{
public:
	virtual void Cook()
	{
		cout << "佛跳墙" << endl;
	}
	virtual void Work()
	{
		cout << "Work" << endl;
	}
	int _a = 0;
};
class Son : public Dad
{
public:
	/*virtual void Cook()
	{
		cout << "方便面" << endl;
	}*/
	int _b = 0;
};
void Test(Dad& p)
{
	p.Cook();
}
int main()
{
	Dad dad;
	Test(dad);
	Son son;
	Test(son);
	return 0;
}
 
父类有虚函数 运行时才确定函数地址 只不过子类没有重写 虚函数表存放的均为父类的虚函数地址 所以均调用父类的成员函数
2.4虚析构和纯虚析构
1.虚析构
1.一般情况:调用完子类析构后自动调用父类析构
class Person 
{
public:
	Person()
	{
		cout << "Person()" << endl;
	}
    ~Person() 
	{
		cout << "~Person()" << endl;
	}
	//int* _ptr;
};
class Student : public Person 
{
public:
	Student()
	{
		cout << "Student()" << endl;
	}
	// 析构函数名底层为:destructor -- 构成虚函数重写
	~Student() 
	{
		cout << "~Student()" << endl;
	}
};
int main()
{
	Student s;
	return 0;
}
 

 2.没有虚析构导致错误的场景
class Person 
{
public:
	Person()
	{
		cout << "Person()" << endl;
	}
	~Person() 
	{
		cout << "~Person()" << endl;
	}
	//int* _ptr;
};
class Student : public Person 
{
public:
	Student()
	{
		cout << "Student()" << endl;
	}
	// 析构函数名底层为:destructor -- 构成虚函数重写
	~Student() 
	{
		cout << "~Student()" << endl;
	}
};
int main()
{
	Person* ptr1 = new Person;
	delete ptr1;
	Person* ptr2 = new Student;
	delete ptr2;
    
	return 0;
}
 

 3.成功样例
class Person 
{
public:
	Person()
	{
		cout << "Person()" << endl;
	}
	virtual ~Person() 
	{
		cout << "~Person()" << endl;
	}
	//int* _ptr;
};
class Student : public Person 
{
public:
	Student()
	{
		cout << "Student()" << endl;
	}
	// 析构函数名底层为:destructor -- 构成虚函数重写
	virtual ~Student() 
	{
		cout << "~Student()" << endl;
	}
};
int main()
{
	Person* ptr1 = new Person;
	delete ptr1;
	Person* ptr2 = new Student;
	delete ptr2;                  //ptr2 -> destructer();
                                  //operator delete(ptr2);
	return 0;
}
 

2.纯虚析构
class Dad 
{
public:
    Dad()
    {
        cout << "Dad 构造函数调用!" << endl;
    }
    //虚析构函数
    //virtual ~Dad()
    //{
    //    cout << "Dad 虚析构函数调用!" << endl;
    //}
    //**虚析构和纯虚析构都必须有函数【实现】
    //纯虚析构函数
    virtual ~Dad() = 0;
    virtual void Name() = 0;
};
Dad::~Dad()
{
    cout << "Dad 纯虚析构函数调用!" << endl;
}
class Son : public Dad
{
public:
    Son(string name)
    {
        cout << "Son 构造函数调用!" << endl;
        _name = new string(name);
    }
   
    ~Son()
    {
        cout << "Son 析构函数调用!" << endl;
        if (this->_name != NULL) 
        {
            delete _name;
            _name = NULL;
        }
    }
    virtual void Name()
    {
        cout << *_name << "是son的名字" << endl;
    }
public:
    string* _name;
};
int main() 
{
    Dad* dad = new Son("Mike");
    dad->Name();
    delete dad;
    return 0;
}
 

3.虚析构/纯虚析构存在的意义
- 虚析构语法:
virtual ~类名(){}
纯虚析构语法:virtual ~类名() = 0;类名::~类名(){} - 二者目的皆是能够【delete指向子类对象的父类指针】时正确调用析构函数。
区别在于:纯虚析构适用于:当前基类作为一个抽象类,不想要实例化对象,只作为子类的父类,并且可以强制子类重写析构函数。
但是使用虚析构和纯虚析构需要注意:二者必须有函数实现–虚析构在类内即可完成函数实现–纯虚析构需要在类外完成。
如果子类中没有堆区数据,可以不写为虚析构或纯虚析构 
2.5总结及拓展
1.final:修饰父类虚函数,表示该虚函数不能再被重写

2.override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错


3.重载、覆盖(重写)、隐藏(重定义)
重载:
两个函数在同一作用域
 函数名相同参数不同(类型个数顺序 )
重写(覆盖):
两个函数分别在基类和派生类的作用域
 R_函数名/参数/返回值都必须相同(协变例外)
 两个函数必须是虚函数
重定义(隐藏):子类和父类中有同名成员,直接访问时将屏蔽父类同名成员
两个函数分别在基类和派生类的作用域
 函数名相同
 两个基类和派生类的同名函数不构成重写就是重定义
2.6抽象类
通常多态中父类虚函数的实现是毫无意义的,主要都是调用子类重写的内容
 因此可以将虚函数改为纯虚函数
 纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0 ;
 当类中有了纯虚函数,这个类称为抽象类
 抽象类特点:
- 无法实例化对象
 - 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
 
1.概念
包含纯虚函数的类叫做抽象类(接口类),抽象类不能实例化出对象。
 派生类只有重写纯虚函数后才能实例化对象。
- 纯虚函数规范了派生类必须重写
 - 纯虚函数体现出了接口继承
 
2.接口继承和实现继承
普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实
 现。
 虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口【返回值函数名参数列表】,目的是为了重写,达成
 多态,继承的是接口。如果不实现多态,不要把函数定义成虚函数。
3.代码演示
class Plant
{
public:
	virtual void Breath() = 0;
};
class Rose :public Plant
{
public:
	virtual void Breath()
	{
		cout << "Rose-疏影暗香" << endl;
	}
};
class Grass :public Plant
{
public:
	virtual void Breath()
	{
		cout << "Grass-生生不息" << endl;
	}
};
void Test()
{
	Plant* pRose = new Rose;
	pRose->Breath();
	Plant* pGrass = new Grass;
	pGrass->Breath();
}
int main()
{
	Test();
	return 0;
}
 







![[uni-app]微信小程序隐私保护指引设置的处理记录](https://img-blog.csdnimg.cn/5804e9d69e334bed9fd8f34001d2d917.png)












