多继承会造成 菱形继承** 使用虚继承来解决
不是给爷爷类加 也不是给子类加 是给父类加 虚基指针和虚基表
多态
概念:
概念: 一个事物的多种形态,简称多态
如:
对象的多态
张三
在对象面前 怂
在朋友面前 谄媚
在父母面前 孝顺
事的多态
吃饭
中国人用筷子
美国人用刀叉
印度人用手抓
事务的多态
- 上行: 子类对象转换为父类对象
- 下行 : 父类对象转换为子类对象
事的多态
- 重载:
- 重写:
- 重定义:
物的多态:
上行:
特点: 自动转换 无风险
概念:子类对象转换为父类对象
语法:
子类对象 转换为 父类对象
- 父类名 &父类对象名 = 子类对象;
子类对象指针 转换为 父类对象指针
- 父类名 *父类对象指针 = 子类对象指针;
示例:
注意:
子类对象转换为父类对象或父类对象指针后,使用转换后父类对象或父类对象指针 无法使用子类特有的成员
包括 成员对象 和 成员函数
下行:
概念: 父类对象 转换 为 子类对象 父类对象指针 转换为 子类对象指针
特点: 有风险 需要强制转换 语法就带
语法:
子类名 &子类对象 = ( 子类名& )父类对象名;
- 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 ; // 纯虚函数 1 virtual 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;
}