类的多态

运算符重载

不能重载的运算符

  • 类属关系运算符“.”
  • 成员指针运算符“.*”
  • 作用域分辨符“::”
  • 三目运算符“?:”

重载运算符

重载运算符函数必须要有重载的类在参数里面

成员重载运算符

  • 调用时,必须是类对象进行调用,且会将自己自动传入做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;
posted @ 2019-10-24 21:48  Dedicate_labors  阅读(235)  评论(0编辑  收藏  举报
……