• C++ 重载运算符 继承 多态 (超详细)


    (一)重载运算符:

    (1)声明与定义格式
    一般是类内声明,类外定义,虽然可以在类内定义,但 写前面堆一堆不好看!!!
    类内声明

    class Demo
    {
    		返回值类型 operator  运算符(形参表);
    }
    

    类外定义:

    返回类型 Demo(类名)::operator运算符(形参表)
    {
        函数体
    }
    

    (2)双目运算符重载为成员函数
    当重载运算符为双目运算符时,形参表中只有一个参数作为右操作数。当前对象作为左操作数,通过this指针隐式传递给函数,一个例子来介绍。
    实例:
    写到最后突然想起来,用int不能实现浮点数的全部特性0.03就不能实现,所以仅作为一个例子。

    class Myfloat
    {
    	int inter;
    	int deci;
    public:
        Myfloat(int a,int b):inter(a),deci(b){}
    	Myfloat operator+(Myfloat const &temp) const;
    	Myfloat operator-(Myfloat const &temp) const;
    };
    Myfloat Myfloat::operator+(Myfloat const &temp) const
    {
        return Myfloat(inter+temp.inter,deci+temp.deci);
    }
    Myfloat Myfloat::operator-(Myfloat const &temp) const
    {
        return Myfloat(inter-temp.inter,deci-temp.deci);
    }
    

    现在只是重载了加减号,实现了自定义浮点数的运算,但是还不成熟,咱们一点一点来丰满这个代码,这个类。(3)单目运算符重载为成员函数
    此时参数表中没有参数,只有当前对象作为运算符的一个操作数。
    实例:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    class Myfloat
    {
    	int inter;
    	int deci;
    public:
        Myfloat(int a,int b):inter(a),deci(b){}
    	Myfloat operator+(Myfloat const &temp) const;
    	Myfloat operator-(Myfloat const &temp) const;
        Myfloat operator--();
        Myfloat operator++();
        Myfloat operator--(int); //补充一个虚操作数,表示前置操作
        Myfloat operator++(int);
    };
    Myfloat Myfloat::operator+(Myfloat const &temp) const
    {
        return Myfloat(inter+temp.inter,deci+temp.deci);
    }
    Myfloat Myfloat::operator-(Myfloat const &temp) const
    {
        return Myfloat(inter-temp.inter,deci-temp.deci);
    }
    Myfloat Myfloat::operator--()  {return Myfloat(inter--,deci);}
    Myfloat Myfloat::operator++()  {return Myfloat(inter++,deci);}
    Myfloat Myfloat::operator--(int)  {return Myfloat(--inter,deci);}
    

    要区分前置与后置运算要加一个(需操作数)告诉机器是前置还是后置。

    (3) 友元函数重载+重载输入输出流(用的稀烂用的多比较重要)
    在左右操作数类型不同时上述重载方式都不能正常使用,这时候就需要两个操作数,在类外重载,因类外不能直接调用,所以要把该函数声明为类的友元。

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    class Myfloat
    {
    	int inter;
    	int deci;
    public:
        Myfloat(int a,int b):inter(a),deci(b){}
    	Myfloat operator+(Myfloat const &temp) const;
    	Myfloat operator-(Myfloat const &temp) const;
        Myfloat operator--();
        Myfloat operator++();
        Myfloat operator--(int); //补充一个虚操作数,表示前置操作
        Myfloat operator++(int);
        friend ostream& operator<<(ostream out,Myfloat &w) ;
        friend istream &operator>>(istream in,Myfloat &w);
    };
    Myfloat Myfloat::operator+(Myfloat const &temp) const{return Myfloat(inter+temp.inter,deci+temp.deci);}
    Myfloat Myfloat::operator-(Myfloat const &temp) const{return Myfloat(inter-temp.inter,deci-temp.deci);}
    Myfloat Myfloat::operator--()  {return Myfloat(inter--,deci);}
    Myfloat Myfloat::operator++()  {return Myfloat(inter++,deci);}
    Myfloat Myfloat::operator--(int)  {return Myfloat(--inter,deci);}
    Myfloat Myfloat::operator++(int)  {return Myfloat(++inter,deci);}
    ostream& operator<<(ostream out,Myfloat &w)
    {
        out<<w.inter<<'.'<<w.deci;
    }
    istream &operator>>(istream in,Myfloat &w)
    {
        in>>w.inter>>w.deci;
    }
    

    (4)赋值运算符重载用于对象数据的复制
    用非类A类型的值为类A的对象赋值时(当然,这种情况下我们可以不提供相应的赋值运算符重载函数,而只提供相应的构造函数,如更有重载函数会优先调用重载后的赋值运算符)。
    当用类A类型的值为类A的对象赋值,且类A的数据成员中含有指针的情况下,必须显式提供赋值运算符重载函数。

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    class Myfloat
    {
    	int inter;
    	int deci;
    public:
        Myfloat(int a,int b):inter(a),deci(b){}
    	Myfloat operator+(Myfloat const &temp) const;
    	Myfloat operator-(Myfloat const &temp) const;
        Myfloat operator--();
        Myfloat operator++();
        Myfloat operator--(int); //补充一个虚操作数,表示前置操作
        Myfloat operator++(int);
        friend ostream& operator<<(ostream out,Myfloat &w) ;
        friend istream &operator>>(istream in,Myfloat &w);
        Myfloat& operator=(const Myfloat &temp ) ;//写或不写都可以,这种如果按照默认的方式一一对应复制,可以不写。
        Myfloat& operator=(const int &w ) ;
    };
    Myfloat Myfloat::operator+(Myfloat const &temp) const{return Myfloat(inter+temp.inter,deci+temp.deci);}
    Myfloat Myfloat::operator-(Myfloat const &temp) const{return Myfloat(inter-temp.inter,deci-temp.deci);}
    Myfloat Myfloat::operator--()  {return Myfloat(inter--,deci);}
    Myfloat Myfloat::operator++()  {return Myfloat(inter++,deci);}
    Myfloat Myfloat::operator--(int)  {return Myfloat(--inter,deci);}
    Myfloat Myfloat::operator++(int)  {return Myfloat(++inter,deci);}
    ostream& operator<<(ostream out,Myfloat &w)
    {
        out<<w.inter<<'.'<<w.deci;
    }
    istream &operator>>(istream in,Myfloat &w)
    {
        in>>w.inter>>w.deci;
    }
    Myfloat& Myfloat::operator=(const Myfloat &temp)
    {
        inter=temp.inter;
        deci=temp.deci;
        return *this;
    }
    Myfloat& Myfloat::operator=(const int &w)
    {
        inter=w;
        return *this;
    }
    






    (二)基类与派生类

    (1)继承语法形式

    class 派生类名:基类名表
    
    {
    
    数据成员和成员函数声明
    
    }

    基类类名表构成: 访问控制 基类名1 访问控制 基类名2…
    继承多各类时叫做多继承,容易产生二义性,一般不用。

    访问控制有三种
    public:公有继承
    private:私有继承
    protected:保护继承
    实例

     class People { }
     class Student:public People
    

    在这里插入图片描述
    (2)派生类的生成过程

    1. 吸收基类成员:除构造和析构函数外
    2. 改造基类成员:通过在派生类中定义同名成员屏蔽基类成员在派生类中直接调用,仍可以基类指针调用同名成员
    3. .添加新成员

    (3)派生类特点

    1. 子类拥有父类除了父类构造和析构函数,所有的成员函数和成员变量;
    2. 2.子类就是一种特殊的父类;
    3. 子类对象可以当做父类的对象使用;
    4. 子类可以拥有父类没有的方法和属性。

    (4)派生类中的静态数据成员
    基类中定义的静态成员,将被所有派生类共享
    2、基类初始化:
    (5)派生类的初始化

    派生类构造函数声明格式为:
    派生类构造函数(变元表):基类(变元表)、对象成员1(变元表)

    构造函数执行顺序:基类——对象成员(类对象成员的初始化)——派生类
    //一开始不理解,现在理解了
    举个栗子:

    class People
    {
    protected:
        string  name;
        string  xb;
    public:
        People(string a,string b):name(a),xb(b){}
    };
    class Cloth
    {
        string color;
        int mysize;
    public:
        Cloth(string c,int m):color(c),mysize(m){}
    };
    class Student:public People
    {
        string id;
        Cloth coat;
    public:
        Student(string id,string name,string xh,string color,int size) :People(name,xb),coat(color,size),id(id){}
    };
    

    执行顺序跟我写的顺序一样,但不是因为按这个顺序写的原因,就像成员变量初始化,也是按这定义顺序初始化,与自己写的初始化顺序无关。
    构造函数的执行顺序:基类→对象成员→派生类;
    (6)派生类构造函数和析构函数的使用原则

    基类的构造函数和析构函数不能继承
    派生类是否定义析构函数与所属基类无关
    如果基类没有定义构造函数或是定义无参构造函数,派生类可以不定义构造函数。
    如果基类无无参构造函数,派生类必须定义构造函数
    如果派生类基类为连续基类继承,每个派生类只负责直接基类的构造

    (7)派生类析构函数

    与构造函数执行顺序相反,派生-----对象-----基类
    

    (8)赋值兼容原则
    这个规则可以简述为能放基类的地方,放派生类一定可以使用,在程序中需要使用基类对象的地方都可以用公有派生类的对象代替。
    例:

    class Base{};
    class Drived{};
    Base demo1,Drived demo2; 
    demo1=demo2;   //派生类对象可以赋值给基类对象:
    Base&Bdemo3=demo2; //派生类对象可以初始化基类引用;
    Base *Bpo=&demo2;//派生类对象可以赋给指向基类对象的指针;//多态实现的方法
    

    主要是派生类中一定包含基类中所有成员,在使用中,一定可以找到对应成员。

    赋值兼容应注意的问题:

    1. 指向基类的指针可以指向公有派生类的对象,但不允许指向它的私有派生类的对象。
    2. 允许将一个声明为指向基类的指针指向其公有派生类对象,但是不能将一个声明为指向派生类对象的指针指向基类对象。
    3. 声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类的定义的成员。

    可以理解为派生类完全包含基类,指向基类的任何成员,都可以在公有派生类中找到对应的成员对象与之对应,如果是私有继承,能找到但是不能访问。但是派生类中有的对象,基类中不一定会有,所以不能这么操作。







    (三)虚函数与多态:

    (1)多态的概念:
    一个接口,多种使用方法
    (2)封装的作用:
    封装可以是得代码模块化;继承可以扩展已经存在的代码,都是为了代代码重用;
    **(3)多态的目的:**接口重用
    (4)静态联编和动态联编分别表示什么?
    在编译的时候能够确定对象所调用的成员函数的地址则为静态联编,一般的调用方式;
    动态联编:指的是在程序运行的时候动态地进行,根据当时的情况来确定调用哪个同名函数,父类指针指向哪个子类,就调用哪个子类的同名函数,实际上是在运行的时候虚函数的实现;

    #include<iostream>
    using namespace std;
    
    class Basic
    {
    public:
    	void oper1()
    	{
    		printf("1
    ");
    	}
    	virtual void oper2()
    	{
    		printf("2
    ");
    	}
    };
    class Direved : public Basic
    {
    public:
    	void oper1()
    	{
    		printf("3
    ");
    	}
    	void oper2()
    	{
    		printf("4
    ");
    	}
    };
    int main(void)
    {
    	Basic a;
    	Direved b;
    	Basic *p = &a; //   定义一个基类指针	
    	p->oper1();  //基类的oper1
    	p->oper2();  //基类的oper2
    	p = &b;  //基类指针指向了派生类
    	p->oper1();  //不是虚函数,仍时基类oper1
    	p->oper2();  //是虚函数构成多态,是派生类的oper2
    	return 0;
    }
    
    

    运行结果过如下,重点关注是否为虚函数时函数调用的区别。
    在这里插入图片描述
    构成虚函数的必要条件:
    函数返回类型一样,参数表一样,函数名一样,同时需要关键字vitrual,缺一不可。

    class Basic
    {
    	vitrual  void p(){ 函数体};
    }
    class Direved :public Basic //构成虚函数的多态
    {
    	void p(){ 函数体};
    }
    
    class Basic
    {
       void p(){ 函数体};
    }
    class Direved :public Basic //不构成虚函数
    {
    	void p(){ 函数体};
    }
    
    class Basic
    {
       vitrual  void p(){ 函数体};
    }
    class Direved :public Basic //不构成虚函数 
    {
    	void p(int a){ 函数体};
    }
    
    class Basic
    {
       vitrual  void p(){ 函数体};
    }
    class Direved :public Basic //不构成虚函数 
    {
    	double p(){ 函数体};
    }
    

    (5)C++纯虚函数
    1.纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”

    virtual void funtion()=0 
    

    1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
    2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

    Class animals
    {
    	virtual void mingjiao()//动物基类,到底是什么动物呢,该怎么鸣叫?当无法合理的给予基类定义虚函数时,常用纯虚函数。
    	{
    	}
    }
    

    为了解决上述问题,引入了纯虚函数的概念

    Class animals
    {
      virtual void mingjiao()=0}
    

    则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。
    注意含有纯虚函数的类,不能直接声明对象!!!!!!!!

    (6)多态性 实现的两种方式
    a、编译时多态性:通过重载函数实现
    b、运行时多态性:通过虚函数实现。

  • 相关阅读:
    u-boot分析(九)----nand flash初始化|nand flash读写分析
    u-boot分析(八)----串口初始化
    u-boot分析(七)----内存初始化
    u-boot分析(六)----时钟初始化
    u-boot分析(五)----I/D cache失效|关闭MMU和cache|关闭看门狗
    u-boot分析(四)---设置异常向量表|设置SVC模式
    u-boot分析(二)----工作流程分析
    u-boot剖析(一)----Makefile分析
    Arm启动流程解析
    卷积神经网络中感受野的介绍
  • 原文地址:https://www.cnblogs.com/lunatic-talent/p/12798855.html
Copyright © 2020-2023  润新知