类的多态
运算符重载
不能重载的运算符
- 类属关系运算符“.”
- 成员指针运算符“.*”
- 作用域分辨符“::”
- 三目运算符“?:”
重载运算符
重载运算符函数必须要有重载的类在参数里面
成员重载运算符
- 调用时,必须是类对象进行调用,且会将自己自动传入做this
- 双目运算符操作时,该类对象必须出现在左边,以进行调用
- 单目运算符操作时,int有是后置++/--,无是前置++/--
非静态成员
#include
using namespace std; class Clock {
public:
Clock(int hour = 0, int minute = 0, int second = 0);
void showTime() const;
Clock &operator++(); //前置单目运算符
Clock operator++(int); //int用于区分前置还是后置运算符 后置单目运算符
private:
int hour, minute, second;
};void Clock::showTime() const{
cout << hour << ":" << minute << ":" << second << endl;
}Clock::Clock(int hour, int minute, int second)
{
if (0 <= hour && hour < 24 && 0 <= minute && minute < 60 && 0 <= second && second < 60)
{
this->hour = hour;
this->minute = minute;
this->second = second;
}
else {
cout << "Time error!" << endl;
}
}Clock &Clock::operator++() { //前置
second++;
if (second >= 60) {
second -= 60;
minute++;
if (minute >= 60) {
minute -= 60;
hour = (hour + 1) % 24;
}
}
return *this;
}Clock Clock::operator++(int) {
Clock old = this;
++(this); //并未加在old上
return old;
}
int main()
{
Clock myClock(23, 59, 59);
cout << "First time output:";
myClock.showTime();
cout << "show myClock++ : ";
(myClock++).showTime();
cout << "show ++myClock: ";
(++myClock).showTime();
return 0;
}
非成员运算符
用来解决调用对象不是类对象的情况(但参数还是要有类对象存在)
非成员
#include
using namespace std; //非成员函数解决"复数+类"的情况,成员的运算符重载只能做到第一个参数是类类型的
class Complex {
public:
Complex(double r=0.0, double i=0.0):real(r), imag(i){}
friend Complex operator+(const Complex &c1, const Complex &c2);
friend Complex operator-(const Complex &c1, const Complex &c2);
friend ostream & operator<<(ostream &cout, const Complex &c);
private:
double real;
double imag;
};Complex operator+(const Complex &c1, const Complex &c2) {
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}Complex operator-(const Complex &c1, const Complex &c2) {
return Complex(c1.real - c2.real, c1.imag - c2.imag);
}ostream & operator<<(ostream &out, const Complex &c) {
out << "(" << c.real << ", " << c.imag << ")";
return out;
}
int main()
{
Complex c1(5, 4), c2(2, 10), c3;
cout << "c1 = " << c1 << endl;
cout << "c2 = " << c2 << endl;
c3 = c1 - c2;
cout << "c3 = c1 - c2 = " << c3 << endl;
c3 = c1 + c2;
cout << "c3 = c1 + c2 = " << c3 << endl;
return 0;
}
虚成员
虚函数
c++中有静态绑定和动态绑定,虚函数就是动态绑定
- 只有用virtual关键字特别声明的函数才进行动态绑定
- 虚函数必须是非静态的成员函数,虚函数经过派生之后仍然是虚函数
- 派生类中的虚函数还会隐藏基类中同名函数的所有其他重载形式(详见虚表)
- 派生类中可以对基类中的虚成员函数进行覆盖
- 虚函数不能是内联函数(内联函数是静态绑定)
- 当派生类的函数返回值和参数类型与个数和基类的虚函数一样时,该函数被默认为虚函数(即使没有书写virtual)
虚函数声明:
- 只能声明在类中的原函数处
- virtual 函数名(){}
虚函数
#include
using namespace std; class A {
public:
virtual void func();
};void A::func() {
cout << "执行A类func函数" << endl;
}class B :public A {
public:
virtual void func();
};void B::func()
{
cout << "执行B类func函数" << endl;
}
void fc(A &a)
{
a.func();
}int main()
{
B b;
fc(b);
b.func();
return 0;
}
虚析构函数
为什么需要虚析构函数?
- 可能通过基类指针删除派生类对象
- 如果你打算允许其他人通过基类指针调用对象的析构函数(通过delete这样做是正常的),就需要让基类的析构函数成为虚函数,否则执行delete的结果是不确定的
虚析构函数
#include
#include using namespace std;
class Base {
public:
virtual ~Base();
};Base::~Base()
{
cout << "Base destructor" << endl;
}class Derived :public Base {
public:
Derived();
virtual ~Derived();
private:
int *p;
};Derived::Derived()
{
p = new int(0);
}Derived::~Derived()
{
cout << "Derived destructor" << endl;
delete p;
}void fun(Base *b)
{
delete b;
}
int main()
{
Base *b = new Derived();
fun(b);
return 0;
}
void fun(Base *b){
delete b;//静态绑定,只会调用~Base()
}
无论是虚函数还是虚析构函数,基类通过指针或者引用的情况才进行动态绑定,如果直接进行复制初始化,那么基类还是只调用基类的成员
虚表
虚表
- 每个多态类都有一个虚表
- 虚表中有当前类的各个虚函数的入口地址(不是声明了virtual该函数的入口地址就在虚表中)
- 每个对象有一个指向当前类的虚表的指针(虚指针vptr)
动态绑定的实现 - 构造函数中为对象的虚指针赋值
- 通过多态类型的指针或引用调用成员函数时,通过虚指针找到虚表,进而找到所调用的虚函数的入口地址
- 通过该入口地址调用虚函数
抽象类:纯虚函数
- 抽象类不用实现
- 其函数更像是目录
纯虚函数:
vitrual 函数类型 函数名(参数表) = 0;