多态有两类:
- 静态多态:函数重载和运算符重载属于静态多态,复用函数名
- 动态多态:派生类和虚函数实现运行多态
动态多态满足条件:1.有继承关系 2.子类重写父类的虚函数
动态多态使用:父类的指针或者引用 执行子类对象
区别:静态多态地址早绑定-编译阶段确定地址函数
动态多态的函数地址晚绑定-运行阶段确定函数地址
动态多态的根本原理是:
再父类的类内存在一个虚函数表指针(vfptr),指向虚函数表,表内记录着成员函数
在子类中会把虚函数指针和虚函数表继承下来,里面内容一样,但是当父类成员函数成为虚函数后,在子类继承下来的虚函数表中的内容就变成了指向子类的重名成员函数,如下
以下是个计算机类,实现计算机的加减乘的功能
#include<iostream>
using namespace std;
class Caculator {
public:
virtual int getresult() {
return 0;
}
int num1;
int num2;
};
//加法子类
class addcaculator :public Caculator {
int getresult() {
return num1 + num2;
}
};
//减法子类
class subcaculator :public Caculator {
int getresult() {
return num1 - num2;
}
};
//乘法子类
class mulcaculator :public Caculator {
int getresult() {
return num1 * num2;
}
};
void test01() {
//在堆区创建caculator指针,指向addcaculator
Caculator *caculator = new addcaculator;
caculator->num1 = 10;
caculator->num2 = 20;
cout << caculator->num1 << "+" << caculator->num2 << "=" << caculator->getresult() << endl;
//销毁指针指向
delete caculator;
caculator = new subcaculator;
caculator->num1 = 15;
caculator->num2 = 29;
cout << caculator->num1 << "-" << caculator->num2 << "=" << caculator->getresult() << endl;
delete caculator;
caculator = new mulcaculator;
caculator->num1 = 19;
caculator->num2 = 20;
cout << caculator->num1 << "*" << caculator->num2 <<"="<<caculator->getresult() << endl;
}
int main() {
test01();
return 0;
}
纯虚函数和抽象类
在多态中,通常父类中虚函数的实现时毫无意义的,主要是调用子类重写的内容
因此可以将虚函数改为纯虚函数
语法:virtual 返回值类型 函数名 {参数列表}=0;
但类中有了纯虚函数,这个类也被称为抽象类
抽象类特点:
无法实例化对象
子类必须重写抽象类中的纯虚函数,否则也属于抽象类
纯虚函数代码示例
#include<iostream>
using namespace std;
class base{
public:
virtual void func()=0;
};
class son:public base{
public:
virtual void func(){
cout<<"func函数调用"<<endl;
}
};
void test01(){
base *p=new son;
p->func();
}
int main(){
test01();
return 0;
}
虚析构和纯虚析构
多态使用时,如果子类有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方法:将父类的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构的共性:
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
区别:
- 如果是纯虚析构,该类属于抽象类,无法实例化对象
以下代码示例:
#include<iostream>
using namespace std;
#include<string>
class animal {
public:
animal() {
cout << "animal的构造函数" << endl;
}
virtual~animal() { //只有把父类的析构函数设成虚函数,才能在删除父类指针时对子类对象进行析构
cout << "animal的析构函数" << endl;
}
//virtual ~animal() = 0; //用纯虚析构的方法实现,注意要在类外写函数的实现
virtual void speak() = 0;
};
//animal::~animal(){
// cout << "animal的析构函数" << endl;
//}
class cat :public animal {
public:
cat(string name) {
m_name=new string(name);
cout << "cat的构造函数" << endl;
}
~cat() {
if (m_name != NULL) {
delete m_name;
m_name = NULL;
cout << "cat的析构函数" << endl;
}
}
virtual void speak() {
cout <<*m_name<< "小猫在说话" << endl;
}
string *m_name;
};
void test01() {
animal *p = new cat("汤姆");
p->speak();
delete p;
}
int main() {
test01();
return 0;
}
不能重载的运算符:①类属关系运算符"."②成员指针运算符".*"③作用域分辨符"::"④三目操作符"?:"
加号运算符重载
作用:实现两个自定义类型的数据相加运算
#include<iostream>
using namespace std;
class person {
public:
//成员函数重载
person operator+(person &p) {
person temp;
temp.m_a = this->m_a + p.m_a;
temp.m_b = this->m_b + p.m_b;
return temp;
}
int m_a;
int m_b;
};
//全局函数重载
person operator+(person &p1, person &p2) {
person temp;
temp.m_a = p1.m_a + p2.m_a;
temp.m_b = p1.m_b + p2.m_b;
return temp;
}
int main() {
person p1;
p1.m_a = 10;
p1.m_b = 10;
person p2;
p2.m_a = 20;
p2.m_b = 30;
//成员函数重载本质person p3 = p1.operator+(p2);
//全局函数重载本质person p3=operator+(p1,p2)
person p3 = p1 + p2;
cout << p3.m_a << endl << p3.m_b << endl;
return 0;
}
注意:运算符重载也可以发生函数重载(比如计算不同数据类型相加)
左移运算符重载
#include<iostream>
using namespace std;
class person {
public:
//利用成员函数重载 左移操作符,p.operator<<(cout) 简化版本 p<<cout
//不会利用成员函数重载<<运算符
/*void operator<<(cout) {
}*/
int m_a;
int m_b;
};
//全局函数重载<<
ostream& operator<<(ostream &out, person &p) {
out << "m_a=" << p.m_a << " m_b=" <<p.m_b;
return out;
}
int main() {
person p;
p.m_a = 10;
p.m_b = 10;
cout << p << endl;
return 0;
}
前置后置 加加运算符重载
#include<iostream>
using namespace std;
class integer{
public:
//前置加加
//friend ostream& operator<<(ostream &out, integer &p);
//后置加加
friend ostream& operator<<(ostream &out, integer p);
integer() {
num = 0;
}
//重载前置++
integer &operator++() {
num++;
return *this;
}
//重载后置++
//int可以用于区分前置后置++
integer operator++(int) {
integer temp = *this;
num++;
return temp;
}
private:
int num;
};
//前置加加
//ostream& operator<<(ostream &out, integer &p) {
// out << p.num;
// return out;
//}
//后置加加
ostream& operator<<(ostream &out, integer p) {
out << p.num;
return out;
}
int main() {
/*integer p;
cout << ++p<<endl;*/
integer p1;
cout <<p1++<<endl;
cout << p1;
}
赋值运算符重载
c++编译器至少给一个类添加四个函数
1.默认构造函数(无参,函数体为空
2.默认析构函数 (无参,函数体为空)
3.默认拷贝构造函数,对属性进行简单的值拷贝
4.赋值运算符operator=,对属性进行值拷贝
#include<iostream>
using namespace std;
class person {
public:
person(){}
person(int age) {
this->age = new int(age);
}
~person() {
if (age != NULL) {
delete age;
age = NULL;
}
}
//重载=
person &operator=(person &p) {
//应先判断是否有属性在堆区,如果有先释放干净,然后在深拷贝
if (age != NULL) {
delete age;
age = NULL;
}
age = new int(*p.age);
return *this;
}
int *age;
};
int main() {
person p1 = 18;
person p2 = 20;
cout << "p1的年龄:" << *p1.age << endl;
cout << "p2的年龄:" << *p2.age << endl;
person p3;
p3=p2 = p1;
cout << "p1的年龄:" << *p1.age << endl;
cout << "p2的年龄:" << *p2.age << endl;
cout << "p3的年龄:" << *p3.age << endl;
return 0;
}
关系运算符重载
作用:让两个自定义数据类型对象进行对比操作
#include<iostream>
using namespace std;
class person {
public:
person(string name, int age) {
this->name = name;
this->age = age;
}
//重载==
bool operator==(person &p) {
if (this->name== p.name&&this->age== p.age) {
return true;
}
else {
return false;
}
}
//重载!=
bool operator!=(person &p) {
if (this->name == p.name&&this->age == p.age) {
return false;
}
else {
return true;
}
}
string name;
int age;
};
int main() {
person p1("汤姆",14);
person p2("汤姆",15);
if (p1!=p2) {
cout << "p1和p2不相等" << endl;
}
else {
cout << "p1和p2相等" << endl;
}
return 0;
}
函数调用运算符()重载
- 函数调用运算符()也可以重载
- 由于重载后的使用方法非常像函数的调用,因此称为仿函数
- 仿函数没有固定的写法,很灵活
#include<iostream>
using namespace std;
#include<string>
class print {
public:
//重载函数调用运算符
void operator()(string test) {
cout<< test << endl;
}
//另一个重载函数调用运算符
int operator()(int num1, int num2) {
return num1 + num2;
}
};
void test02() {
cout << print()(100, 200)<<endl; //利用匿名对象(执行完该行,对象就被释放,匿名对象可以由类名加()创建
}
int main() {
print p;
p("hello world");
test02();
return 0;
}