类的多态

运算符重载

不能重载的运算符

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

重载运算符

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

成员重载运算符

  • 调用时,必须是类对象进行调用,且会将自己自动传入做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;
}

    

类的多态_第1张图片

非成员运算符

用来解决调用对象不是类对象的情况(但参数还是要有类对象存在)

非成员

#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;
}

    

类的多态_第2张图片

虚成员

虚函数

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;
}


    

类的多态_第3张图片

虚析构函数

为什么需要虚析构函数?

  • 可能通过基类指针删除派生类对象
  • 如果你打算允许其他人通过基类指针调用对象的析构函数(通过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;
}

     
    

类的多态_第4张图片

void fun(Base *b){
    delete b;//静态绑定,只会调用~Base()
}

无论是虚函数还是虚析构函数,基类通过指针或者引用的情况才进行动态绑定,如果直接进行复制初始化,那么基类还是只调用基类的成员

虚表

虚表

  • 每个多态类都有一个虚表
  • 虚表中有当前类的各个虚函数的入口地址(不是声明了virtual该函数的入口地址就在虚表中)
  • 每个对象有一个指向当前类的虚表的指针(虚指针vptr)
    动态绑定的实现
  • 构造函数中为对象的虚指针赋值
  • 通过多态类型的指针或引用调用成员函数时,通过虚指针找到虚表,进而找到所调用的虚函数的入口地址
  • 通过该入口地址调用虚函数

抽象类:纯虚函数

  • 抽象类不用实现
  • 其函数更像是目录

纯虚函数:

vitrual 函数类型 函数名(参数表) = 0;

你可能感兴趣的:(类的多态)