5) 类型转换运算符: 当需要将当前类型的对象转换为指定的类型的数据的时候需要使用类型转换运算符(强制类型转换)
类型转换运算符()的重载函数的格式:
operator 类型名()
注意: 类型转换运算符函数原型中没有返回类型,参数列表为空,但是在函数体内一定要有返回值,返回值的类型是函数原型中指定的类型名
//类型转换运算符重载
operator int()
{
Date start(1970, 1, 1);
//使用减法运算得到两个日期之间的天数,然后再转化为秒数
return (*this - start) * 24 * 60 *60;
}
测试代码:
Date today(2023, 8, 5);
cout << (int)today << endl; //使用类型转换运算符将Date类型的对象转换为int类型的数据
cout << time(0) << endl;
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_06 运算符重载> g++ .\运算符重载.cpp -o test
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_06 运算符重载> ./test
1667001600
1691199100
注意: 类型转换运算符只针对于基本类型,对用户自定义类型没有定义
6) 下标运算符
下标运算符[]使用数组或者指针指向的连续空间,用下标运算符的序号来给数组元素进行赋值或者取值
下标运算符重载函数的原型:
type 类名::operator[](形参)
下标运算符重载函数的形参是表示元素序号的整数
下标运算符只能重载为成员函数
/*
* @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @Date: 2023-08-05 09:40:05
* @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @LastEditTime: 2023-08-05 10:03:06
* @FilePath: \vscode\cpp_source\CPP_202308\CPP_07 运算符重载&多态\下标运算符.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
#include <iostream>
using namespace std;
typedef int ElemType;
class MyArray
{
private:
ElemType *m_pElem; //指向动态数组的起始位置的指针
int m_nlen; //动态数组中元素的个数
public:
MyArray();
MyArray(int n);
~MyArray();
MyArray &operator = (const MyArray &array);
ElemType &operator [](int index);
friend void show(MyArray &array);
};
MyArray::MyArray()
{
m_nlen = 0;
m_pElem = new ElemType[1];
}
MyArray::MyArray(int n)
{
m_nlen = n;
m_pElem = new ElemType[n];
}
MyArray::~MyArray()
{
delete[] m_pElem;
}
MyArray &MyArray::operator = (const MyArray &array)
{
if(m_pElem == array.m_pElem)
{
return *this;
}
//如果两个动态数组对象都是已经存在的,那么要将一个动态数组的空间内容赋值给另外一个
if(NULL != m_pElem)
{
delete[] m_pElem;
}
this->m_nlen = array.m_nlen; //普通数据成员可以直接赋值
m_pElem = new ElemType[array.m_nlen];
for(int i = 0; i < m_nlen; i++)
{
m_pElem[i] = array.m_pElem[i];
}
return *this;
}
ElemType &MyArray::operator [](int index)
{
return m_pElem[index];
}
void show(MyArray &array)
{
for(int i = 0; i < array.m_nlen; i++)
{
cout << array[i] << " ";
}
cout << endl;
}
int main()
{
MyArray arr(8);
for(int i = 0; i < 8; i++)
{
arr[i] = i + 3;
}
show(arr);
MyArray temp;
show(temp);
temp = arr;
show(temp);
return 0;
}
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_07 运算符重载&多态> ./test
3 4 5 6 7 8 9 10
3 4 5 6 7 8 9 10
7) 输入输出运算符(流运算符)
输入运算符: >> cin实际上是一个istream类型的对象
输出运算符: << cout实际上是一个ostream类型的对象
cin >> a;
cout << a << b << endl;
>>的第一个操作数是istream类型的对象, 第二个操作数是当前对象
<<的第一个操作数是ostream类型的对象,第二个操作数是当前对象
所以输入输出运算符都只能重载为友元函数
输出运算符的原型:
friend ostream & operator <<(ostream &out, Type &t);
friend istream & operator >>(istream &in, Type &t);
/*
* @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @Date: 2023-08-05 09:40:05
* @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @LastEditTime: 2023-08-05 10:26:49
* @FilePath: \vscode\cpp_source\CPP_202308\CPP_07 运算符重载&多态\下标运算符.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
#include <iostream>
using namespace std;
typedef int ElemType;
class MyArray
{
private:
ElemType *m_pElem; //指向动态数组的起始位置的指针
int m_nlen; //动态数组中元素的个数
public:
MyArray();
MyArray(int n);
~MyArray();
MyArray &operator = (const MyArray &array);
ElemType &operator [](int index);
friend void show(MyArray &array);
friend ostream &operator << (ostream &out, MyArray &array);
friend istream &operator >> (istream &in, MyArray &array);
};
MyArray::MyArray()
{
m_nlen = 0;
m_pElem = new ElemType[1];
}
MyArray::MyArray(int n)
{
m_nlen = n;
m_pElem = new ElemType[n];
}
MyArray::~MyArray()
{
delete[] m_pElem;
}
MyArray &MyArray::operator = (const MyArray &array)
{
if(m_pElem == array.m_pElem)
{
return *this;
}
//如果两个动态数组对象都是已经存在的,那么要将一个动态数组的空间内容赋值给另外一个
if(NULL != m_pElem)
{
delete[] m_pElem;
}
this->m_nlen = array.m_nlen; //普通数据成员可以直接赋值
m_pElem = new ElemType[array.m_nlen];
for(int i = 0; i < m_nlen; i++)
{
m_pElem[i] = array.m_pElem[i];
}
return *this;
}
ElemType &MyArray::operator [](int index)
{
return m_pElem[index];
}
void show(MyArray &array)
{
for(int i = 0; i < array.m_nlen; i++)
{
cout << array[i] << " ";
}
cout << endl;
}
ostream &operator << (ostream &out, MyArray &array)
{
for(int i = 0; i < array.m_nlen; i++)
{
out << array[i] << " ";
}
out << endl;
return out;
}
istream &operator >> (istream &in, MyArray &array)
{
cout << "请输入" << array.m_nlen << "个元素: " << endl;
for(int i = 0; i < array.m_nlen; i++)
{
in >> array.m_pElem[i];
}
return in;
}
int main()
{
MyArray arr(8);
/*
for(int i = 0; i < 8; i++)
{
arr[i] = i + 3;
}
*/
cin >> arr;
//show(arr);
cout << arr;
MyArray temp;
//show(temp);
cout << temp;
temp = arr;
//show(temp);
cout << temp;
return 0;
}
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_07 运算符重载&多态> g++ .\下标运算符.cpp -o test
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_07 运算符重载&多态> ./test
请输入8个元素:
5 3 8 2 9 6 7 1
5 3 8 2 9 6 7 1
5 3 8 2 9 6 7 1
多态: 同一种行为对于不同类型(这里的不同类型一般指同一个类的不同派生类)的对象会有不同的反应
赋值兼容的情况:
a. 派生类对象赋值给基类对象
b. 基类指针指向派生类对象
c. 基类引用派生类对象
Base b;
Drivded d;
Base *p = &d;
#include <iostream>
using namespace std;
class Animal
{
public:
void sleep()
{
cout << "动物在睡眠" << endl;
}
void breath()
{
cout << "动物在呼吸" << endl;
}
};
class Fish : public Animal
{
public:
void breath()
{
cout << "鱼在呼吸" << endl;
}
};
int main()
{
Fish ftj;
Animal *p;
p = &ftj;
p->breath();
return 0;
}
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_07 运算符重载&多态> g++ .\多态测试.cpp -o test
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_07 运算符重载&多态> ./test
动物在呼吸
从程序运行结果可以看出,基类指针指向派生类对象的时候得到的是派生类对象中的那个基类对象,通过这个指针调用的方法是基类对象中的方法(从基类继承的版本),不能调用指针所指向的那个实际对象自己实现的方法
当在基类中定义了虚函数的时候,在对象的内存地址的起始位置会增加一个虚表指针,这个指针指向当前对象的虚函数表,虚函数表中是当前对象的所有虚函数的入口地址
派生类继承基类的时候同样的会继承基类的虚表指针,这个指针指向派生类对象的虚函数表,在派生类的虚函数表中存储的是派生类对象的虚函数的入口地址(如果在派生类中没有重新实现那么虚函数表中的入口地址就是从基类继承的虚函数的入口地址;如果派生类中对从基类继承的虚函数进行了重写或者重载,虚函数表中就会使用自己重载或者重写的函数的入口地址替换从基类继承的虚函数的入口地址);派生类中不是从基类继承的虚函数在虚函数表中的位置是所有继承的虚函数的后面
当一个基类指针指向派生类对象的时候,通过指针访问派生类对象的虚表指针,通过虚表指针访问派生类对象的虚函数表,然后访问的方式就是派生类中重新实现的版本 -- 多态
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void sleep()
{
cout << "动物在睡眠" << endl;
}
//多态的实现是将基类中的成员方法使用virtual修饰,将其定义为虚函数
virtual void breath()
{
cout << "动物在呼吸" << endl;
}
};
class Fish : public Animal
{
public:
void breath()
{
cout << "鱼在呼吸" << endl;
}
virtual void test()
{
cout << "我就看看" << endl;
}
};
class Bee : public Animal
{
public:
void breath()
{
cout << "蜜蜂也要呼吸" << endl;
}
};
void breath(Animal &obj)
{
obj.breath();
}
int main()
{
Fish ftj;
Animal *p;
Bee b;
p = &ftj;
p->breath();
p = &b;
p->breath();
breath(b);
breath(ftj);
/*
cout << sizeof(ftj) << endl;
p->sleep();
//p->test(); //基类指针不能访问派生类中独有的虚函数
ftj.test();
*/
return 0;
}
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_07 运算符重载&多态> g++ .\多态测试.cpp -o test
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_07 运算符重载&多态> ./test
鱼在呼吸
蜜蜂也要呼吸
蜜蜂也要呼吸
鱼在呼吸
使用虚函数实现的多态是运行时多态(动态多态) -- 滞后联编
基类的虚函数在派生类中只能重写,不能重载
虚函数必须是成员函数,并且不能使用static修饰
构造函数不能是虚函数,而析构函数往往都必须是虚函数
//析构函数不是虚函数的时候;
#include <iostream>
using namespace std;
class Animal
{
public:
int *p;
public:
virtual void sleep()
{
cout << "动物在睡眠" << endl;
}
//多态的实现是将基类中的成员方法使用virtual修饰,将其定义为虚函数
virtual void breath()
{
cout << "动物在呼吸" << endl;
}
Animal()
{
p = new int;
}
~Animal()
{
delete p;
cout << "动物析构" << endl;
}
};
class Fish : public Animal
{
private:
int *pp;
public:
void breath()
{
cout << "鱼在呼吸" << endl;
}
virtual void test()
{
cout << "我就看看" << endl;
}
Fish()
{
pp = new int;
}
~Fish()
{
cout << "Fish析构" << endl;
delete pp;
}
};
class Bee : public Animal
{
public:
void breath()
{
cout << "蜜蜂也要呼吸" << endl;
}
};
void breath(Animal &obj)
{
obj.breath();
}
int main()
{
Fish *pfish = new Fish;
delete pfish;
cout << endl;
Animal *p = new Fish;
delete p;
return 0;
}
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_07 运算符重载&多态> ./test
Fish析构
动物析构
动物析构
显然可以看到当基类指针指向派生类对象的时候,释放对象的时候只调用了基类的析构方法,而没有调用派生类的析构方法(只析构了从基类继承的部分,自己独有的部分并没有析构)
如果将基类的析构函数定义为虚函数
virtual ~Animal()
{
delete p;
cout << "动物析构" << endl;
}
程序的运行效果:
Fish析构
动物析构
Fish析构
动物析构
可以看到派生类对象完全析构了
既然析构函数经常都是虚函数,为什么C++在设计的时候不降析构函数默认的就设计为虚函数?
练习:
编写一个乐器Instrument类:
分为:钢琴(Piano)、小提琴(Violin)
各种乐器的弹奏(Play)方法各不相同。
编写一个乐手类BandsMan :
编写方法TestPlay,对各种乐器进行弹奏测试。