当前位置:网站首页>理解多态,让不同的“人”做同一件事情会产生不同的结果
理解多态,让不同的“人”做同一件事情会产生不同的结果
2022-07-24 00:14:00 【小小酥诶】
以下代码在vs2019下的x86程序中,涉及指针为4bytes。
多态的概念
多态:多种形态。具体一些,就是不同的对象去做同样的行为,会产生出不同的状态。
例如:买车票的时候,当你是学生的时候,可能就会半价;当你是其他人的时候可能就是全价了。
//举例,这个代码可以暂时不用看懂
class Person
{
public:
virtual void BuyTicket()
{
cout << "普通票---全价" << endl;
}
};
class Student : public Person
{
virtual void BuyTicket()
{
cout << "学生票---半价" << endl;
}
};
void func(Person& p)
{
p.BuyTicket();
}
int main(void)
{
Person p1;
Student s1;
//不同的对象做同一件事,得到的状态不同
func(p1);
func(s1);
return 0;
}

多态分为:静态的多态、动态的多态。
静态的多态:是在编译时实现的。例如函数重载,看上去像是在调用同一个函数有着不同的行为。
动态的多态:是在运行时实现的。基类的指针或者引用去调用同一个函数,传不同类型的对象会调用不同的函数。
构成多态的条件
1、多态是基于继承的,只有在有继承关系的体系中才能构成多态。
2、必须通过基类的指针或者引用去调用虚函数。
3、被调用的成员必须是虚函数,且派生类必须对基类的虚函数进行重写。
虚函数:被virtual关键字修饰的非静态类成员。(ps:在返回值前面加virtual)
虚函数和重写
虚函数的重写:派生类中有一个和基类基本相同的函数,这里的基本相同指,同样是虚函数并且返回值、函数名、参数都相同。而函数体内的内容不同,类似于将这个函数重新改写了,这种情况称为派生类的虚函数重写了基类的虚函数。
构成多态的一个例外:返回值不同,其他条件满足,并且这两个函数的返回值是继承关系(父子关系)的指针或者引用,这个时候也构成多态。这种情况称为协变
//协变也构成多态
//协变:返回值可以不同,但是必须是继承关系(父子关系)
class Person
{
public:
virtual Person* BuyTicket()
{
cout << "普通票---全价" << endl;
return nullptr;
}
};
class Student : public Person
{
virtual Student* BuyTicket() //派生类中可以不是虚函数
{
cout << "学生票---半价" << endl;
return nullptr;
}
};
void func(Person& p)
{
p.BuyTicket();
}
int main(void)
{
Person p1;
Student s1;
func(p1);
func(s1);
return 0;
}
构成多态的另一个例外:基类成员是虚函数、而派生类成员可以不是虚函数,也就是不用virtual修饰,但是其他条件还是需要满足的。这样也构成多态,虽然派生类成员没有被virtual修饰,但它是先继承了基类虚函数的属性,再完成的重写,那么它也算是虚函数。
class Person
{
public:
virtual void BuyTicket()
{
cout << "普通票---全价" << endl;
}
};
class Student : public Person
{
void BuyTicket() //派生类可以不是虚函数
{
cout << "学生票---半价" << endl;
}
};
void func(Person& p)
{
p.BuyTicket();
}
int main(void)
{
Person p1;
Student s1;
func(p1);
func(s1);
return 0;
}
存在着一个场景,析构函数必须是虚函数:动态申请基类、派生类的对象,如果都交给了基类指针管理,那么析构函数需要是虚函数。
//存在着一种场景,析构函数必须是函数
//基类、派生类对象,都交给了基类指针管理。
class Person
{
public:
virtual void BuyTicket()
{
cout << "普通票---全价" << endl;
}
~Person()
{
cout << "~Person" << endl;
}
};
class Student : public Person
{
virtual void BuyTicket()
{
cout << "学生票---半价" << endl;
}
~Student()
{
cout << "~Student" << endl;
}
};
void func(Person& p)
{
p.BuyTicket();
}
int main(void)
{
//派生类对象交给了基类指针管理
Person* p2 = new Person;
Person* s2 = new Student;
delete p2;
delete s2;
return 0;
}
运行结果:p2和s2都是调用基类的析构函数去清理资源。
派生类对象地址赋值给基类指针,虽然被切片了,但还是需要调用派生类自己的析构函数去清理资源。所以这并没有正确地调用析构函数,我们需要的是,S2调用Student自己的析构函数。析构函数的函数名,编译时会被统一处理成destructor,并且它不需要参数和返回值,所以认为析构函数函数名、参数、返回值相同。那要使得析构函数构成多态,那么可以在用virtual修饰这两个析构函数,或者修饰基类的析构函数就可以了。
class Person
{
public:
virtual void BuyTicket()
{
cout << "普通票---全价" << endl;
}
virtual ~Person() //析构函数修饰成虚函数
{
cout << "~Person" << endl;
}
};
class Student : public Person
{
virtual void BuyTicket()
{
cout << "学生票---半价" << endl;
}
~Student()
{
cout << "~Student" << endl;
}
};
void func(Person& p)
{
p.BuyTicket();
}
int main(void)
{
//派生类对象交给了基类指针管理
Person* p2 = new Person;
Person* s2 = new Student;
delete p2;
delete s2;
return 0;
}

这里调用了两次基类的析构函数,是因为,派生类的析构函数调用之后,会自动调用一次基类的析构函数去析构被继承下来的基类部分。
关键字override 、final
final:修饰虚函数,表示该虚函数不能被重写。
修饰方法:在基类虚函数参数括号后,函数体之前使用。
//关键字final
class Car
{
public:
virtual void Drive() final
{
}
};
class Benz :public Car
{
public:
virtual void Drive()
{
cout << "Benz-舒适" << endl; }
};

【补充】
使用final修饰类,那么会使得该类不能被继承。修饰方法:在基类类名之后,类体之前使用。
//关键字final
class Car final
{
public:
virtual void Drive()
{
}
};
class Benz :public Car
{
public:
virtual void Drive()
{
cout << "Benz-舒适" << endl; }
};

override:修饰虚函数,检查派生类的虚函数是否重写了基类的某个虚函数,如果没有重写编译报错。
修饰方式:在派生类虚函数参数括号后面,函数体之前使用。
//关键字override
//使用override修饰派生类的虚函数,判断该虚函数是否重写,如果没有重写则会编译报错
class Car
{
public:
virtual void Drive()
{
}
};
class Benz :public Car
{
public:
virtual void Drive(int i) override
{
//cout << "Benz-舒适" << endl;
}
};

重载、重写(覆盖)、重定义(隐藏)区分
重载:
1、两个函数在同一作用域中
2、要求函数名相同。参数个数、类型或者顺序不同重写(覆盖):
1、两个函数在不同的作用域。一个在基类的作用域、一个在父类的作用域。
2、函数名、参数、返回值都必须相同(协变除外,返回值可以不同,但是一定是父子关系)
3、两个函数都是虚函数,或者基类中的那个函数是虚函数。重定义(隐藏)
1、两个函数在不同的作用域。一个在基类的作用域、一个在父类的作用域
2、两个函数的函数名、参数相同
3、两个基类和派生类的同名函数不构成重写就是重定义
边栏推荐
- Y75. Chapter IV Prometheus factory monitoring system and practice -- Prometheus alarm setting (VI)
- [speech synthesis] tensorflowtts Chinese text to speech
- mongodb的多数据源配置
- Pytest interface automation testing framework | multi process running case
- [wechat applet] design and interactive implementation of auction product details page (including countdown and real-time update of bids)
- Realize the function of uploading and downloading files and directories similar to RZ and SZ on the native terminal
- Data driven excel reading and writing
- Mysql database foundation
- July 23, 2022 - mapper file description
- 文本和图片的绘制、数据存储、localStorage、sessionStorage、cookie三者的区别
猜你喜欢

The differences between text and image drawing, data storage, localstorage, sessionstorage, and cookies

Linked list - 206. Reverse linked list (this question is very important)

jenkins下使用声明式(Declarative)和Jenkinsfile的方式构建Pipeline流水线项目

kubernetes error

Sentinel link mode flow control failure solution

Copy the customer service wechat, go to wechat to add, and make a call

Redis主从同步机制

Mysql database foundation

Deep learning 9 feedforward neural network 2: realize feedforward neural network and model optimization

webrtc 1对1 -基本架构与目录
随机推荐
How to open an account and buy financial products with 6% income?
Linked list - 707. Design linked list
vulnhub wpwn: 1
My meeting of OA project (query)
2022牛客多校联赛第二场 题解
Pytest interface automated testing framework | how to get help
What is restful verb
尝试新的方法
NGFW portal authentication experiment
July 23, 2022 - mapper file description
Why do most people think programming is difficult?
Multi knapsack explanation of dynamic programming knapsack problem
合宙ESP32C3基于Arduino IDE框架下配置分区表
STL case - judges' scoring
Tool recommendation - YuQue
投资的物质回报
It basic English
Browser cannot access Minio
【语音合成】TensorFlowTTS 中文文本转语音
FPGA - SPI bus control flash (3) including code