C++_20_多态
多继承会造成 菱形继承** 使用虚继承来解决
不是给爷爷类加 也不是给子类加 是给父类加 虚基指针和虚基表
多态
概念:
概念: 一个事物的多种形态,简称多态
如:
对象的多态
张三
在对象面前 怂
在朋友面前 谄媚
在父母面前 孝顺
事的多态
吃饭
中国人用筷子
美国人用刀叉
印度人用手抓
事务的多态
- 上行: 子类对象转换为父类对象
- 下行 : 父类对象转换为子类对象
事的多态
- 重载:
- 重写:
- 重定义:
物的多态:
上行:
特点: 自动转换 无风险
概念:子类对象转换为父类对象
语法:
子类对象 转换为 父类对象
- 父类名 &父类对象名 = 子类对象;
子类对象指针 转换为 父类对象指针
- 父类名 *父类对象指针 = 子类对象指针;
示例:
注意:
子类对象转换为父类对象或父类对象指针后,使用转换后父类对象或父类对象指针 无法使用子类特有的成员
包括 成员对象 和 成员函数
下行:
概念: 父类对象 转换 为 子类对象 父类对象指针 转换为 子类对象指针
特点: 有风险 需要强制转换 语法就带
语法:
子类名 &子类对象 = ( 子类名& )父类对象名;
- Cat cat = (Cat &) anim; // Cat &强制转换
子类名 子类对象指针 = ( 子类名 * ) 父类对象指针名;
事的多态
重载
- 同一个类中函数名相同,形参列表不同,称为重载
重写
- 继承关系中,子类成员函数与父类成员函数函数名相同,形参列表相同,返回值类型相同
重定义
- 函数名相同,即为重定义 c不允许重定义 C++允许
虚函数:
概念: 使用 virtual 修饰的函数
语法:
virtual 返回值类型 函数名(){函数体;}
原理: 使用 虚基指针和虚基表
特定:
-
当子类重写 父类 提供的 虚 函数
-
当子类对象转换为父类对象 ,使用转换的父类对象调用 重写的虚函数,此时执行的子类重写后的
未使用 虚函数 重写父类的函数后 造成想要的效果出现不了
发现打印的是A 不是想要的B
解决方式
虚函数: virtual 父类函数 加 virtual
#include <iostream> using namespace std; // 虚函数 解决 继承重写的问题 class A { public:virtual void method(){cout << " 父类 A" << endl;} };class B : public A { public:void method(){cout << " 子类 B" << endl;} }; int main(int argc, char const *argv[]) {B b;A &a = b; // 上行 将 b对象 转为 A 类对象a.method();return 0; }// 有问题 没出预期效果 没出 B 应该出 B
- 虚函数 是有函数体的 不知道才写纯虚 函数 知道就写虚函数 要是不知道函数体就写
纯 虚函数 并子类重写 父类的所有纯虚函数 不然创建不了对象
纯虚函数:
- 没有函数体
- 如果一个类中有虚函数,该类无法直接创建对象,这种类称为抽象类
- 子类继承于抽象类,要么重写所有纯虚函数,要么
概念:
- 没有函数体的虚函数
- 如果一个类中有纯虚函数,该类无法直接创建对象,这种类称为抽象类子类继承与抽象类,要么重写抽象类中所有的纯虚函数要么自己也是抽象类
语法:
virtua]返回值类型函数名(形参列表)= 0;
使用完 释放内存的时候 担心 内存释放的不干净
父类 anim 子类dog 释放dog 出现问题 出现 只释放了 anim 内存 而没释放 dog
释放 anim 就两者都释放掉了 但就是担心你释放的时候写错 忘记释放内存 使用虚析构造解决
#include <iostream> using namespace std;// 纯虚函数class A{ public: virtual void me01() = 0 ; // 纯虚函数 1virtual void me02() = 0 ; // 纯虚函数 2 只有重写父类的纯虚函数之后才能创建对象};class B :public A{public://重写父类所有纯虚函数 重写的不是全部的时候 就创建不了对象void me01(){cout<<"B 01"<<endl;}void me02(){cout<<"B 02"<<endl;}};int main(int argc, char const *argv[]) {B b; //创建对象才正常 return 0;}
虚析构造:【*】
概念:
- 使用 virtual 修饰的析构函数
特点:
- 子类对象指针转换为父类对象指针,此时delete父类对象指针,只会调用父类析构函数
- 如果父类析构函数为虚析构造,那么也将调用子类析构函数
语法: 【注意 是给父类中写 】
virtual ~父类名()
{}
示例:
纯虚析构:【**】
优化虚析函数版的
因为要分文件 所以优化用这个
概念 : 没有函数体的虚析构造
语法 :
virtual ~父类名()= 0;
注意 :
- 类外实现
- 纯虚析构也是纯虚函数
- 顾纯虚析构的类也是抽象类,此时无法创建对象
案例:
小张是个宠物医生
张女士 养了一只狗叫旺财
李女士 养了一只猫叫布丁
分析:
存在的对象
小张 张女士 李女士 旺财 布丁
类:
动物:人类 医生类 狗类 猫类
A 动物类:
成员变量: name
成员函数: 1 speak 2 无参构造 3 有参构造 4 拷贝构造 5 纯虚析构
B 人类:
特有成员: 宠物 [anim]
重写父类speak函数
无参构造 有参构造 拷贝构造 纯虚析构
C 医生类:
特有函数: work:治病
无参构造
有参构造
拷贝构造
纯虚析构
D 狗类
重写父类speak函数
无参构造
有参构造
拷贝构造
纯虚析构
E 猫类
重写父类speak函数
无参构造
有参构造
拷贝构造
纯虚析构
==============
创建对象 建立关系
分文件编写:
头文件:
anim.h
#ifndef xx
#define xx
#include <iostream>
#include <stdlib.h>
#include <string.h>
class Anim
{
private:char *name;public:Anim();Anim(char *name);Anim(const Anim &anim);virtual ~Anim(); // 纯虚析构 virtual ~父类名()= 0;virtual void speak(char *info) = 0; // 纯虚函数 virtua]返回值类型函数名(形参列表)= 0;char *getName() const;void setName(char *name);
};
#endif
person.h
#ifndef tt
#define tt
#include <iostream>
#include "anim.h"
using namespace std;
class Person : public Anim
{private:Anim* anim;public:Person();Person(char *name);Person(char *name,Anim* anim);Person(const Person &p);~Person() ; // 纯虚函数 virtua]返回值类型函数名(形参列表)= 0;Anim* getAnim() const;void setAnim(Anim* anim);void speak(char *info);
};
#endif
doctor.h
#ifndef do
#define do
#include <iostream>
#include "person.h"
class Doctor : public Person
{
public:Doctor();Doctor(char *name);// virtual ~Doctor() = 0;~Doctor();void work(Anim *anim);
};
#endif
dog.cpp
#ifndef dogg
#define dogg
#include <iostream>
#include "anim.h"
class Dog : public Anim
{
public:Dog();Dog(char *name);// virtual ~Dog() = 0; 使用这个有问题~Dog();void speak(char *info);
};
#endif
cat.h
#ifndef catt
#define catt
#include <iostream>
#include "anim.h"
class Cat : public Anim
{
public:Cat();Cat(char *name);~Cat();void speak(char *info);
};
#endif
源文件
anim.cpp
#include "anim.h"
Anim::Anim() : name(NULL) {};
Anim::Anim(char *name)
{int len = strlen(name);this->name = (char *)calloc(len + 1, 1);strcpy(this->name,name);
}
Anim::Anim(const Anim &anim)
{int len = strlen(anim.name);this->name = (char *)calloc(len + 1, 1);strcpy(this->name,anim.name);
}
Anim::~Anim()
{if (name != NULL){free(name);name = NULL;}
}
char * Anim::getName() const
{return name;
}void Anim::setName(char *name)
{if (this->name != NULL){free (this->name);}int len = strlen(name);this->name = (char *)calloc(len + 1, 1);strcpy(this->name,name);
}
person.cpp
#include "person.h"
Person::Person() : anim(NULL) {}
Person::Person(char *name) : Anim(name), anim(NULL) {}
Person::Person(char *name, Anim *anim) : Anim(name), anim(anim) {}
Person::Person(const Person &person)
{// 因为形参 person 使用了const 修饰 意味着不能修改其成员// 此时使用person调用 其函数 系统担心 函数内部对其成员进行修改,// 顾调用 的函数必须是使用const 修饰的成员函数this->anim = person.getAnim(); char *name = person.getName(); //加const
}
Person::~Person() {} // 纯虚析构
Anim *Person::getAnim() const
{return anim;
}
void Person::setAnim(Anim *anim)
{this->anim = anim;
}
void Person::speak(char *info)
{cout << this->getName() << " "<<endl;
}
doctor.cpp
#include "doctor.h"
Doctor::Doctor() {}
Doctor::Doctor(char *name) : Person(name) {}
Doctor::~Doctor() {}
// 给宠物看病 将宠物传进来
void Doctor::work(Anim *anim)
{cout << this->getName() << "给" << anim->getName() << "看看病!" << endl;anim->speak(NULL);
}
cat.cpp
#ifndef catt
#define catt
#include <iostream>
#include "anim.h"
class Cat : public Anim
{
public:Cat();Cat(char *name);~Cat();void speak(char *info);
};
#endif
dog.cpp
#include "dog.h"
using namespace std;
Dog::Dog() {};
Dog::Dog(char *name) : Anim(name) {}
Dog::~Dog() {}
void Dog::speak(char *info)
{cout << getName() << ":" << "汪汪汪" << endl;
}
执行文件:
main.cpp
#include <iostream>
#include "anim.h"
#include "doctor.h"
#include "cat.h"
#include "dog.h"
#include "person.h"
using namespace std;
int main(int argc, char const *argv[])
{Doctor *d = new Doctor("小张");Dog *dog = new Dog("旺财");Cat *cat =new Cat("大橘");Person* p1 = new Person("张女士",dog);Person* p2 = new Person("李女士",cat);d->work(p1->getAnim());d->work(p2->getAnim());return 0;
}