zoukankan      html  css  js  c++  java
  • 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、运行时多态性:通过虚函数实现。

  • 相关阅读:
    python中的编码问题
    CVPR2018 Tutorial 之 Visual Recognition and Beyond
    hdu 1376 Octal Fractions
    hdu 1329 Hanoi Tower Troubles Again!
    hdu 1309 Loansome Car Buyer
    hdu 1333 Smith Numbers
    hdu 1288 Hat's Tea
    hdu 1284 钱币兑换问题
    hdu 1275 两车追及或相遇问题
    hdu 1270 小希的数表
  • 原文地址:https://www.cnblogs.com/lunatic-talent/p/12798855.html
Copyright © 2011-2022 走看看