• C++ 多态


    C++ 多态
    jcLee95 的个人博客
    邮箱 :291148484@163.com
    CSDN 主页https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343
    本文地址https://blog.csdn.net/qq_28550263/article/details/125348483

    目 录

    1. 多态的概念

    2. 函数的重载

    3. 运算符的重载

    4. 虚函数

    5. 抽象类


    1. 多态的概念

    1.1 什么是多态

    在编程中,多态的体现就是一个操作接口具有表现多种不同形态的能力,对不同的输入有不同的处理方式。例如, + 运算符,既可以适用于整数的相加,也可以适用于浮点数的相加,这就是多态性的体现。

    1.2 C++ 多态实现原理

    C++中的多态是是通过绑定 来实现的,其中绑定指将一个标识符名称与一段函数代码结合起来。依据绑定实现的时期,可以分为 编译时的绑定运行时的绑定

    编译时的绑定 ,故名思意,在编译器对代码进行编译的时候就已经完成了绑定。而 运行时的绑定 需要等到程序运行的时候才将标识符和相应的行数代码结合起来。

    2. 函数的重载

    在C++中 函数的重载 是一种静态多态性,是通过 编译时绑定 来完成的。

    3. 运算符的重载

    在C++中 运算符的重载 是一种动态多态性,是通过 运行时绑定 来完成的。

    3.1 概述

    运算符重载让我们能够使用系统里面预先定义好的运算符进行相应运算。例如 定义一个 复数类,这是C++原生类型中不包含的类型。那么我们若想要通过C++中的运算符来完成复数的运算,则需要重载相应的运算符来实现。

    C++ 几乎可以重载全部C++中已经有的运算符,但不能重载以下运算符:

    运算符描述
    .成员访问运算符;
    .*”、->*成员指针访问运算符;
    ::域运算符;
    sizeof长度运算符;
    ?:条件运算符。

    3.2 双目运算符 重载

    重载运算符是通过定义类的成员函数来实现的,重载函数名是由关键字 operator 和其后要重载的运算符符号构成的。其格式示意如下:

    函数类型 operator 运算符(形参列表){
     // 函数体
    }
    
    • 1
    • 2
    • 3

    例如,定义一个复数类:

    class Complex {
      private:
        double r;    // 实部
        double i;    // 虚部
       
      public:
        // 构造一个复数对象:指定实部和虚部的值作为该复数对象的实部和虚部
        Complex(double r = 0.0, double i = 0.0) : r(r), i(i) { }
        
        Complex operator+(const Complex& c2) const {
            // 将一个临时无名复数对象用作返回值 
            return Complex(r + c2.r, i + c2.i);
        }
        
        Complex operator-(const Complex& c2) const {
            // 同理
            return Complex(r - c2.r, i - c2.i);
        }
    
        void print() const {
            cout <<  r << " + " << i << "j" << endl;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    与其他函数一样,重载运算符有一个返回类型和一个参数列表。除后置++、–外,其中形参的个数为该运算符的操作数的个数减1。

    3.3 单目运算符 重载

    单目运算符是只对一个操作数进行操作的运算符。如位运算符、自增/自减运算符

    3.3.1 前置单目运算符 的重载

    如果要重载 U 为类 成员函数,使之能够实现表达式 U oprd,其中 oprd 为A类对象,则 U 应被重载为 A 类的成员函数,无形参。

    经重载后,表达式 U oprd 相当于 oprd.operator U()

    例如:

    
    
    • 1

    3.3.2 后置单目运算符 ++-- 的重载

    如果要重载 ++或–为类成员函数,使之能够实现表达式 oprd++oprd-- ,其中 oprd 为A类对象,则 ++或-- 应被重载为 A 类的成员函数,且具有一个 int 类型形参。

    经重载后,表达式 oprd++ 相当于 oprd.operator ++(0)

    例如:

    
    
    • 1

    3.4 关系运算符重载

    关系运算符包括 <><=>===。现在我们通过重载关系运算符来实现两个复数类实例的关系比较。在此我们定义:

    • 两个复数的实部和虚部相等,则认为两个复数相等(==为真);
    • 若复数a的模比复数b的模大,则表示方式 a>b 返回真,反之返回假;
    • 若复数a的模等于复数b的模 或 复数 a 的模大于复数 b 的模,则表示方式 a >= b 返回真。类似地,同理定义 a <= b。

    实现和调用测试如下:

    #include <iostream>
    #include <cmath>
    using namespace std;
    
    class Complex {
      private:
        double r;    // 实部
        double i;    // 虚部
    
      public:
        
        Complex(double r = 0.0, double i = 0.0) : r(r), i(i) { }
    
        Complex operator+(const Complex& c2) const {
            return Complex(r + c2.r, i + c2.i);
        }
    
        Complex operator-(const Complex& c2) const {
            return Complex(r - c2.r, i - c2.i);
        }
    
        // 计算复数的模长
        double length()
        {
            return pow((pow(r, 2)+pow(i,2)),0.5);
        }
    
        // 重载关系运算符 " == " 
        bool operator == (const Complex cplx)
        {
            if(r == cplx.r && i == cplx.i)
            {
                return true;
            }else{
                return false;
            }
        }
    
        // 重载关系运算符 " > " 
        bool operator > (Complex cplx){
    	    if(length() > cplx.length()){
                return true;
    	    }
        	return false;
    	    
        }
    
        // 重载关系运算符 " < " 
        bool operator < (Complex cplx){
            if (length() < cplx.length()) {
                return true;
            }
        	return false;
            
        }
    
        // 重载关系运算符 " >= " 
        bool operator >= (Complex cplx) {
            double length = cplx.length();
            if (this->length() > length || this->length() == length) {
                return true;
            }
            
        	return false;
            
        }
    
        // 重载关系运算符 " >= " 
        bool operator <= (Complex cplx) {
            double length = cplx.length();
            if (this->length() < length || this->length() == length) {
                return true;
            }
            
        	return false;
            
        }
    
        void print() const {
            cout << r << " + " << i << "j" << endl;
        }
    };
    
    int main(){
        Complex c1(3, 4), c2(5, 3), c3(4, 3);
    
        cout << "c1.length() = " << c1.length() << "\n";
        cout << "c2.length() = " << c2.length() << "\n";
        cout << "c3.length() = " << c3.length() << "\n";
    
        if (c1 > c2) { cout << "c1 > c2\n"; }
        else if(c1 < c2){ cout << "c1 < c2\n"; }
        else if (c1 == c2) { cout << "c1 == c2\n"; }
    
        if(c1 >= c2){ cout << "c1 >= c2\n"; }
        else if (c1 <= c2) { cout << "c1 <= c2\n"; }
    
        // 是否相等(虚部实部同时相等)
        if(c1 == c3){
            cout << "c1 == c3\n";
        }else{
            cout << "c1 != c3\n";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104

    Out[]:

    c1.length() = 5
    c2.length() = 5.83095
    c3.length() = 5
    c1 < c2
    c1 <= c2
    c1 != c3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.5 输入/输出运算符重载

    C++中我们使用 流提取运算符 >>流插入运算符 <<输入输出 内置的数据类型。

    我们可以通过重载 流这两个运算符 ,以实现将他们用于自定义类型,表示提取和插入操作。

    为了不需要创建对象,而直接调用函数,我们可以把运算符重载函数声明为类的友元函数。

    例如,为了方便直接在控制台输入输出复数,我们为复数类重载输入输出运算符:

    #include <iostream>
    #include <cmath>
    using namespace std;
    
    class Complex {
      private:
        double r;    // 实部
        double i;    // 虚部
    
      public:
        
        Complex(double r = 0.0, double i = 0.0) : r(r), i(i) { }
    
        Complex operator+(const Complex& c2) const {
            return Complex(r + c2.r, i + c2.i);
        }
    
        Complex operator-(const Complex& c2) const {
            return Complex(r - c2.r, i - c2.i);
        }
    
        // 重载运算符 " << "
        friend ostream& operator<<(ostream& output, const Complex& c){
            output << c.r << " + " << c.i << "i";
            return output;
        }
    
        // 重载运算符 " >> "
        friend istream& operator>>(istream& input, Complex& D)
        {
            input >> D.r >> D.i;
            return input;
        }
    
    };
    
    int main(){
        Complex c1(3, 4), c2;
        cout << "c1 = " << c1 << endl;
        cout << "Please enter the value of c2(real and image) : " << endl;
        cin >> c2;
        cout << "c2 = " << c2;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    Out[]:

    c1 = 3 + 4i
    Please enter the value of c2(real and image) :
    
    
    • 1
    • 2
    • 3

    输入:

    6 7
    
    • 1

    Out[]:

    c2 = 6 + 7i
    
    • 1

    如图所示:
    在这里插入图片描述

    3.6 赋值运算符重载

    3.7 函数调用运算符 ()的 重载

    函数调用运算符 () 可以被重载用于类的对象,例如:

    #include <iostream>
    using namespace std;
    
    class Point {
      private:
        int x;
        int y;
    
      public:
        // 构造函数
    	Point(int x0, int y0) {
            x = x0;
            y = y0;
        }
        // 默认构造函数
    	Point(){
            x = 0;
            y = 0;
        }
    
        void print() {
            cout << "x = " << x << "; y = " << y << endl;
        }
    
        // 重载运算符函数
        Point operator()(int stepx1, int stepy1, int stepx2, int stepy2) {
            Point M;
            M.x = x + stepx1 - stepx2;
            M.y = y + stepy1 - stepy2;
            return M;
        }
    };
    
    int main(){
        Point p1(10, 10), p2;
        cout << "Point 1: ";
    	p1.print();
        p2 = p1(20,5,15,3);
        cout << "Point 2: ";
    	p2.print();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    Out[]:

    Point 1: x = 10; y = 10
    Point 2: x = 15; y = 12
    
    • 1
    • 2

    可见,通过重载(),我们可以创建一个能够传递任意数目参数的运算符函数,本例中相当于传入了两个点的坐标。

    3.8 下标运算符 []的 重载

    下标运算符 [] 通常用于访问数组元素。最典型的应用场景,当我们自定义一个数据容器类如自定义一个数组类、链表类等等,想通过 下标运算符 [] 访问其中的某个元素时,这时我们重载[] 来实现。

    例如,我们实现一个动态数组:

    #include <iostream>
    typedef int Rank;
    using namespace std;
    #define INIT_CAPACITY 3
    
    template <typename T> class Vector {
    protected:
        Rank _size;
        int _capacity;
        T* _elem;
    
        // 扩容
        void _expand() {
            if (_size < _capacity)return;
            if (_capacity < INIT_CAPACITY) {
                _capacity = INIT_CAPACITY;
            };
            T* oldElem = _elem;
            _elem = new T[_capacity <<= 1];
            for (int i = 0; i < _size; i++) {
                _elem[i] = oldElem[i];
            }
            delete[] oldElem;
        }
    
        // 缩容
        void _shrink() {
            if (_capacity < INIT_CAPACITY << 1)return;
            if (_size << 2 * _capacity)return;
            T* oldElem = _elem = new T[_capacity >>= 1];
            for (int i = 0; i < _size; i++) {
                _elem[i] = oldElem[i];
            }
            delete[] oldElem;
        }
    
    public:
        // 构造
        Vector(int c = INIT_CAPACITY, int s = 0, T v = 0) {
            _elem = new T[_capacity = c];
            for (_size = 0; _size < s; _elem[_size++] = v);
        }
    
        // 析构
        ~Vector() {
            delete[] _elem;
        }
    
        // 重载 下标运算符 []
        T& operator[] (Rank r)const {
            return _elem[r];
        }
    
        // 插入元素
        Rank insert(Rank r, T const& e){
            _expand();// 扩容
            // 移动元素
            for (int i = _size; i > r; i--){
    	        _elem[i] = _elem[i - 1];
            }
            _elem[r] = e; // 放入新元素
            _size++; // 容量加1
            return r;
        }
        // 尾插
        Rank append(T const& e){
            return insert(_size, e);
        }
    };
    
    int main(){
        Vector<int> a;
        a.append(1);
        a.append(3);
        a.append(5);
        a.append(7);
        a.append(9);
        cout << "a[0] = " << a[0] << endl;
        cout << "a[3] = " << a[3] << endl;
        cout << "a[4] = " << a[4] << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    Out[]:

    a[0] = 1
    a[3] = 7
    a[4] = 9
    
    • 1
    • 2
    • 3

    3.9 类成员访问运算符 ->的 重载

    类成员访问运算符( -> )被定义用于为一个类赋予 “指针” 行为,

    
    
    • 1

    4. 虚函数

    虚函数 是在基类中使用关键字 virtual 声明的函数,它能够用于实现 动态绑定。其声明格式为:

    virtual 函数类型 函数名(形参表);
    
    • 1

    派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。虚函数必须是非静态的成员函数,虚函数经过派生之后,就可以实现运行过程中的多态。

    在基类中声明为virtual的函数在从基类派生的所有类中都是虚函数,不论派生类中是否使用virtual进行声明。

    使用对象调用虚函数总是进行静态解析,只有通过指针或者引用调用的虚函数,才会进行动态解析

  • 相关阅读:
    【进程、线程和进程间通信】(三)进程间通信
    (已知中后序、先中序遍历)恢复二叉树
    MATLAB编程:绘制折线图 以及 画图的一些小技巧
    2024考研计算机考研复试-每日重点(第二十期)
    了解一下事务的概念
    [攻防世界]BABYRE
    JavaScript模块化
    设计模式——行为型模式
    武汉某母婴用品公司 - 集简云连接ERP和营销系统,实现库存管理的自动化
    差分信号变送器模块使用说明
  • 原文地址:https://blog.csdn.net/qq_28550263/article/details/125348483