Day42、操作符重载、继承、向上造型

    xiaoxiao2023-03-24  3

    练习:

    实现3*3矩阵类,支持如下操作符:

    +  - +=   -=

     

    #include<iostream>

    #include<iomanip>//setw()设置域宽

    using namespace std;

    class M33{

    public:

       M33(void){

           for(int i=0;i<3;i++)

               for(int j=0;j<3;j++)

                    m_a[i][j]=0;

        }

       M33(int a[][3]){

           for(int i=0;i<3;i++)

               for(int j=0;j<3;j++)

                    m_a[i][j]=a[i][j];

        }

       friend ostream& operator<<(ostream& os,const M33& m){

           for(int i=0;i<3;i++){

               for(int j=0;j<3;j++)

                    os<<setw(4)<<m.m_a[i][j];

                    os<<endl;

           }

       return os;

        }

       const M33 operator+(const M33& m)const{

           int a[3][3]={};

           for(int i=0;i<3;i++)

               for(int j=0;j<3;j++)

                    a[i][j]=m_a[i][j]+m.m_a[i][j];

           return a; //return M33(a);

        }

       const M33 operator-(const M33& m)const{

           int a[3][3]={};

           for(int i=0;i<3;i++)

               for(int j=0;j<3;j++)

                    a[i][j]=m_a[i][j]-m.m_a[i][j];

           return a; //return M33(a);

        }

       const M33 operator*(const M33& m)const{

           int a[3][3]={};

           for(int i=0;i<3;i++)

               for(int j=0;j<3;j++)

                    for(int k=0;k<3;k++)

                       a[i][j]+=m_a[i][k]*m.m_a[k][j];

           return a; //return M33(a);

        }

        //+ - *返回值右值,左操作数必须是左值,右操作数可以为左值,也可以为右值

       M33& operator+=(const M33& m){

           return *this=*this + m;

        }

       M33& operator-=(const M33& m){

           return *this=*this - m;

        }

       M33& operator*=(const M33& m){

           return *this=*this * m;

        }

    private:

       int m_a[3][3];

    };

    int main(){

       int a1[3][3]={1,2,3,4,5,6,7,8,9};

       int a2[3][3]={9,8,7,6,5,4,3,2,1};

       M33 m1(a1);

       M33 m2(a2);

       cout<<m1<<endl;

       cout<<m2<<endl;

       cout<<"m1+m2"<<endl;

       cout<<m1+m2<<endl;

       cout<<"m1-m2"<<endl;

       cout<<m1-m2<<endl;

       cout<<"m1*m2"<<endl;

       cout<<m1*m2<<endl;

       cout<<"m1+=m2"<<endl;

       cout<<(m1+=m2)<<endl;

       cout<<"m1-=m2"<<endl;

       cout<<(m1-=m2)<<endl;

       cout<<"m1*=m2"<<endl;

       cout<<(m1*=m2)<<endl;

       return 0;

    }

    tarena@tarena-virtual-machine:~/day42$./a.out

      1   2   3

      4   5   6

      7   8   9

     

      9   8   7

      6   5   4

      3   2   1

     

    m1+m2

     10  10  10

     10  10  10

     10  10  10

     

    m1-m2

      -8  -6  -4

     -2   0   2

      4   6   8

     

    m1*m2

     30  24  18

     84  69  54

     138114  90

     

    m1+=m2

     10  10  10

     10  10  10

     10  10  10

     

    m1-=m2

      1   2   3

      4   5   6

      7   8   9

     

    m1*=m2

     30  24  18

     84  69  54

     138114  90

    3、(操作符重载,接昨天)单目操作符重载:#O

    3.1 计算类的单目操作符(~ 、 !…) !a, ~a ; -a; &a ;

    1)成员函数形式

    #O==》O.operator#( )

    2)全局函数形式

    #O==》::operator#(0)

    举例:

      1#include<iostream>

      2using namespace std;

      3class Integer{

      4public:

     5     Integer(int i=0):m_i(i){}

     6     const Integeroperator-(void)const{//取负操作符重载

     7         return Integer(-m_i);

     8     }

     9     //自定义~表示乘方

     10    friend const Integer operator~(const Integer& i){

     11        return Integer(i.m_i*i.m_i);

     12     }

     13    friend ostream& operator<<(ostream& os,const Integer&i){

     14        os<<i.m_i;

     15     }

     16private:

     17    int m_i;

     18};

     19int main(void){

     20    Integer i(100);

     21    Integer j=-i;

     22    cout<<i<<endl;//-100

     23    cout<<j<<endl;//-100

    24     j=~i;

     25    cout<<j<<endl;//10000

     26     return 0;

     27 }

     

    3.2自增减操作符

    1)前缀自增减

    成员函数:  #O==》O.operator#( )

    全局函数:  #O==》::operator#(0)

    ++n;  操作数必须是左值,返回值就是操作数自身也是左值

    2)后缀自增减

    成员函数:  #O==》O.operator#( int)

    全局函数:  #O==》::operator#(0,int)

    int num=10;

    num++;

    num ; 11

    操作数必须是左值,返回值是右值,是自增减之前的副本

    举例:

      1#include<iostream>

      2using namespace std;

      3class Integer{

      4public:

     5     Integer(int i=0):m_i(i){}

     6     //前++  (可以用友元) 成员函数

     7     Integer&operator++(void){

     8         ++m_i;

     9         return *this;

     10     }

     11    //前--  全局函数

     12    friend Integer& operator--(Integer& i){

     13        --i.m_i;

     14        return i;

     15     }

     16    //后++   (可以用友元friend,这样也可以)

     17    const Integer operator++(int){

     18        Integer old=*this;

     19        ++*this;//复用前++

     20        return old;

     21     }

     22    //后--

     23     friend const Integeroperator--(Integer& i,int){

    24         Integer old=i;

     25        --i;

     26        return old;

     27     }

     28    friend ostream& operator<<(ostream& os,const Integer&i){

     29        os<<i.m_i;

     30     }

     31private:

     32    int m_i;

     33};

     34int main(void){

     35    Integer i(123);

     36    Integer j=++i;

     37    cout<<"i="<<i<<endl;//124

     38    cout<<"j="<<j<<endl;//124

    39     j=++++++i;

     40    cout<<"i="<<i<<endl;//127

     41    cout<<"j="<<j<<endl;//127

     42

     43    j=i++;

     44    cout<<"i="<<i<<endl;//128

     45    cout<<"j="<<j<<endl;//127

     46

     47    j=--i;

     48    cout<<"i="<<i<<endl;//127

     49    cout<<"j="<<j<<endl;//127

     50

     51    j=i--;

     52    cout<<"i="<<i<<endl;//126

     53    cout<<"j="<<j<<endl;//127

     54    return 0;

     55 }

    4、函数操作符:()(少用!)

    如果一个类重载了函数操作符,那么该类的对象就可以当做函数来调用,其参数和返回值就是函数操作符函数的参数和返回值

      1#include<iostream>

      2using namespace std;

      3class Square{

      4public:

     5     double operator()(doublex)const{

     6         return x*x;

     7     }

     8     int operator()(int a,intb,int c=9){

     9         return a+b-c;

     10     }

     11};

     12int main(void){

     13    Square square;

     14    cout<<square(13.)<<endl;//square.operator()(13.)

     15    cout<<square(10,40,20)<<endl;//30

     16    cout<<square(10,40)<<endl;//41

     17    return 0;

     18 }

    5、下标操作符,[]

    int arr[5]={1,2,3,4,5};

    arr[1]=20;

    cosnt int (rarr)[5]= arr;

    rarr[1]=20;//error

    举例:

      1#include<iostream>

      2using namespace std;

      3class Array{

      4public:

     5     Array(int size):m_data(newint[size]),m_size(size){}

     6     ~Array(void){

     7         delete[] m_data;

     8         m_data=0;

     9     }

     10    int& operator[](int i){//非常对象调用

     11        return m_data[i];

     12     }

     13    const int& operator[](int i)const{//常对象调用

     14        //return m_data[i];

     15        //直接复用上面的非常版本

     16         returnconst_cast<Array&>(*this)[i];

     17     }

     18private:

     19    int * m_data;

     20    int m_size;

     21};

     22int main(void){

     23    Array a(10);

    24    a[0]=10;//a.operator[](0)->a.m_data[0]=10

     25    a[1]=20;

     26    cout<<a[0]<<','<<a[1]<<endl;//10,20

     27    const Array& ra=a;

     28    cout<<ra[0]<<','<<ra[1]<<endl;//10,20

     29    //ra[0]=11; error const修饰不能修改值

     30    //ra[1]=12; error

     31    return 0;

     32 }

     

    6、解引用和间接成员访问操作符: * ->

    用于实现智能指针:一个封装常规指针的类类型对象,当它离开作用域时,其析构函数负责释放该常规指针所指向的动态内存,避免内存泄露。

    代码:

      1#include<iostream>

      2using namespace std;

      3class A{

      4public:

     5     A(void){

     6         cout<<"A::A()"<<endl;

     7     }

     8     ~A(void){

     9        cout<<"A::~A()"<<endl;

     10     }

     11};

     12class PA{

     13public:

     14    PA(A* pa=NULL):m_pa(pa){}

     15    ~PA(void){

     16        if(m_pa){

     17            delete m_pa;    //释放栈区空间

     18            m_pa=NULL;

     19        }

     20     }

     21private:

     22    A* m_pa;

     23};

    24 int main(void){

     25    //A a;

     26    //A* pa=new A;

     27    PA pa(new A);//栈区对象

     28    return 0;//智能指针:析构函数一定会被调用,防止内存泄露

     29 }

    tarena@tarena-virtual-machine:~/day42$./a.out

    A::A()

    A::~A()

    实际使用:

    #include<iostream>

    #include<memory>

    using namespace std;

    class A{

    public:

       A(const string& str):m_str(str){

           cout<<"A::A()"<<endl;

       }   

       ~A(void){

           cout<<"A::~A()"<<endl;

       }  

       string m_str;

    };

    class PA{

    public:

       PA(A* pa=NULL):m_pa(pa){}

       ~PA(void){

           if(m_pa){

               delete m_pa;    //释放栈区空间

               m_pa=NULL;

           }

        }

       A* operator->(void)const{

           return m_pa;

        }

       A& operator*(void)const{

           return *m_pa;

        }

    private:

       A* m_pa;

    };

    int main(void){

       //A a;

       //A* pa=new A;

    #if 0

       PA pa(new A("hello world"));//栈区对象

       //pa.operator->()->m_str;

       cout<<pa->m_str<<endl;

       //pa.operator*().m_str;

       cout<<(*pa).m_str<<endl;

    #else

       auto_ptr<A> pa(new A("hello world!"));//内部自动封装了只能指针

       cout<<pa->m_str<<endl;

       cout<<(*pa).m_str<<endl;

    #endif

       return 0;//智能指针:析构函数一定会被调用,防止内存泄露

    }

    7、类型转换操作符

    1)通过构造函数实现自定义类型转换

    基本类型-à类类型

    Integer i (100);

    //int -> Integer

    i=200;//通过类型转换构造函数

    2)通过类型转换操作符函数实现自定义类型转换

    operator 目标类型(void)const { }

    类类型à基本类型

    //Integer àint

    int i2=i ;//类型转换操作符函数 

    3)如果两个类型都是基本类型,无法转换

    4)如果两个类型都是自定义类型,两种方式都可以

    例:

      1#include<iostream>

      2using namespace std;

      3class Integer{

      4public:

     5     //int ---> Integer

     6     Integer(int data=0):m_data(data){

     7         cout<<"类型转换构造函数"<<endl;

     8     }

     9     operator int(void)const{

     10        cout<<"类型转换操作符函数"<<endl;

     11        return m_data;

     12     }

     13private:

     14    int m_data;

     15};

     16int main(){

     17    Integer i1(100);//构造函数

     18    i1=200;//类型转换构造函数,把i1转换成整型数

     19    cout<<i1<<endl;//200  操作符函数

     20    int i2=i1;//操作符函数

     21    cout<<i2<<endl;//200

     22    return 0;

     23 }

    类型转换构造函数

    类型转换构造函数

    类型转换操作符函数

    200

    类型转换操作符函数

    200

     

    8、new/delete 操作符

    static void* operator new(size_t size){….}

    static void operator delete(void *p){….}

    举例:

      1#include<iostream>

      2#include<cstdlib>

      3using namespace std;

      4class A{

      5public:

     6     A(void){

     7        cout<<"A::A()"<<endl;

     8     }

     9     ~A(void){

     10        cout<<"A::~A()"<<endl;

     11     }

     12    static void* operator new(size_t size){

     13        cout<<"A::new"<<endl;

     14        void *pv=malloc(size);

     15        return pv;

     16     }

     17    static void operator delete(void* pv){

     18        cout<<"A::delete"<<endl;

     19        free(pv);

     20     }

     21};

     22int main(){

     23    //1)分配内存

     24     //A* pa=(A*)A::operator new(sizeof(A));

     25    //2)pa->A();

     26    A* pa=new A;

     27

     28    //1)pa->~A();

     29    //2)释放内存

     30    //A::operator delete(pa);

     31    delete pa;

     32     return0;

     33 }

    9、操作符重载限制

    1)不是所有的操作符都能重载,下列操作符无法重载:

     作用域限定操作符:“::”

     直接成员访问操作符:“.”

     直接成员指针解引用操作符:“.*”

     条件操作符:“?”

     字节长度操作符:“sizeof( )”

     类型信息操作符:“typeid”

    2)如果一个操作符所有的操作数都是基本类型,则无法重载

    3)操作符重载不会改变编译器预定义的优先级

    4)操作符重载无法改变操作数的个数

    5)无法通过操作符重载发明新的操作符

    6)操作符重载完全可以用普通成员函数来替换

    一、继承(重点)!!!!Inheritance

    1、继承的基本概念

     通过一种机制表达类型之间共性和特性的方式,利用已有数据类型定义新的数据类型,这种机制就是继承。

    人类:姓名、年龄、吃饭、睡觉

    学生类:姓名、年龄、学号、吃饭、睡觉、学习

    教师类:姓名、年龄、工资、吃饭、睡觉、讲课

     

    人类:姓名、年龄、吃饭、睡觉

    学生类继承人类:学号、学习

    教师类继承人类:工资、讲课

     

       人类(基类/父类)

      /        \

    学生类    教师类(派生类/子类)

    基类派生子类,子类继承基类

    2、继承的语法形式

    class 子类:继承方式1  基类1,继承方式2,基类2……{

    …..

    }

    继承方式:

    public :公有继承

    protected :保护继承

    private:私有继承

     

    class Human{};

    class Student:public Human{

           //Student类继承了Human类

           //Student类存在一份Human中的成员

    };

    举例

    #include<iostream>

    using namespace std;

    //人类(基类)

    class Human{

    public:

       Human(const string& name,int age)

           :m_name(name),m_age(age){}//初始化

       void eat(const string& food)const{//常函数(const *this)不需要修改成员变量

           cout<<"我在吃"<<food<<endl;

        }

       void sleep(int time)const{

           cout<<"我睡了"<<time<<"小时"<<endl;

        }

    protected: //保护成员可以在子类中访问,外部不能访问

       string m_name;

       int m_age;

    };

    //学生类(人类派生的子类)

    class Student:public Human{

    public:

       Student(const string& name,int age,int no):

           Human(name,age),m_no(no){}//初始化

       void who(void)const{//不修改成员变量的都尽量使用常函数

           cout<<"我叫"<<m_name<<",今年"<< \

               m_age<<"岁,学号"<<m_no<<endl;

        }

       void learn(const string& course)const{

           cout<<"我在学"<<course<<endl;

        }

    private:

       int m_no;

    };

    //教师类(人类派生的另外一个子类)

    class Teacher:public Human{

    public:

        Teacher(conststring& name,int age,double salary):

           Human(name,age),m_salary(salary){}

       void teach(const string& course)const{

           cout<<"我在讲"<<course<<endl;

        }

       void who(void)const{

           cout<<"我叫"<<m_name<<",今年"<< \

               m_age<<"岁,工资:"<<m_salary<<endl;

        }

    private:

       double m_salary;

    };

    int main(void){

       Student s("悟空",30,10001);

       s.who();

       s.eat("桃子");

       s.sleep(8);

       s.learn("打坐");

       Teacher t("唐僧",31,5000.5);

       t.who();

       t.eat("蔬菜");

       t.sleep(6);

       t.teach("佛法");

       return 0;

    }

    tarena@tarena-virtual-machine:~/day42$./a.out

    我叫悟空,今年30岁,学号10001

    我在吃桃子

    我睡了8小时

    我在学打坐

    我叫唐僧,今年31岁,工资:5000.5

    我在吃蔬菜

    我睡了6小时

    我在讲佛法

    3、公有继承的特性

    3、1共同性:任何时候子类对象都可以看做是一个基类对象

    32向上造型(重点)

    将子类类型的指针或引用转换成基类类型的指针或引用。

    (将学生类型转换成老师类型)

    这种转换缩小了指针或引用操作范围,在编译看来是安全的,所以可以隐式转换。

    #include<iostream>

    using namespace std;

    //人类(基类)

    class Human{

    public:

       Human(const string& name,int age)

           :m_name(name),m_age(age){}//初始化

       void eat(const string& food)const{//常函数(const *this)不需要修改成员变量

           cout<<"我在吃"<<food<<endl;

        }

       void sleep(int time)const{

           cout<<"我睡了"<<time<<"小时"<<endl;

        }

    protected: //保护成员可以在子类中访问,外部不能访问

       string m_name;

       int m_age;

    };

    //基类子对象:子类对象中包含基类的部分

    //学生类(人类派生的子类)

    class Student:public Human{

    public:

       Student(const string& name,int age,int no):

           Human(name,age),m_no(no){}//初始化

       void who(void)const{//不修改成员变量的都尽量使用常函数

           cout<<"我叫"<<m_name<<",今年"<< \

               m_age<<"岁,学号"<<m_no<<endl;

        }

       void learn(const string& course)const{

           cout<<"我在学"<<course<<endl;

        }

    private:

       int m_no;

    };

    //教师类(人类派生的另外一个子类)

    class Teacher:public Human{

    public:

       Teacher(const string& name,int age,double salary):

           Human(name,age),m_salary(salary){}

       void teach(const string& course)const{

           cout<<"我在讲"<<course<<endl;

        }

       void who(void)const{

           cout<<"我叫"<<m_name<<",今年"<< \

               m_age<<"岁,工资:"<<m_salary<<endl;

        }

    private:

       double m_salary;

    };

    int main(void){

       Student s("悟空",30,10001);

       s.who();

       s.eat("桃子");

       s.sleep(8);

       s.learn("打坐");

       Teacher t("唐僧",31,5000.5);

       t.who();

       t.eat("蔬菜");

       t.sleep(6);

       t.teach("佛法");

     

       //向上造型,缩小了指针的操作范围,所以安全,可以做隐式类型转换

       //(12个字节只访问前8个字节,安全!)

       Human* ph=&s; //ph指向子类对象的基类指针

       ph->eat("桃");//可以访问基类中有的部分

       ph->sleep(10);//可以访问基类中有的部分

       //ph->who();

       return 0;

    }

     

    图:向上造型

     

     

    练习:M33矩阵类让它增加支持如下操作符:

    前后++  前后--   取负(让无参构造对象减去有参构造对象)、取下标[ ]值

    arr[1][1]=>*(arr+1)[1]=>*(*(arr+1)+1)

     

     

    #include <iostream>

    #include <iomanip>//setw()设置域宽

    using namespace std;

     

    class M33{

    public:

           M33(void){

                  for(inti=0; i<3; i++)

                         for(intj=0; j<3; j++)

                                m_a[i][j]= 0;

           }

           M33(inta[][3]){          

                  for(inti=0; i<3; i++)

                         for(intj=0; j<3; j++)

                                m_a[i][j]= a[i][j];

           }

           friendostream& operator<<(ostream& os,

                         constM33& m){

                  for(inti=0; i<3; i++){

                         for(intj=0; j<3; j++)

                                os<< setw(4) << m.m_a[i][j];     

                         os<< endl;

                  }

                  returnos;

           }

           //+- *:返回值右值,左右操作数可以右值也可

           //以为左值

           constM33 operator+(const M33& m)const{

                  inta[3][3]={};

                  for(inti=0; i<3; i++)

                         for(intj=0; j<3; j++)

                                a[i][j]=m_a[i][j]+m.m_a[i][j];

                  returna;

           }

           constM33 operator-(const M33& m)const{

                  inta[3][3]={};

                  for(inti=0; i<3; i++)

                         for(intj=0; j<3; j++)

                                a[i][j]=m_a[i][j]-m.m_a[i][j];

                  returna;

           }

           constM33 operator*(const M33& m)const{

                  inta[3][3]={};

                  for(inti=0; i<3; i++)

                         for(intj=0; j<3; j++)

                                for(intk=0; k<3; k++)

                                      a[i][j]+=

                                             m_a[i][k]*m.m_a[k][j];

                  returna;

           }

           //+=-= *=:返回值左值,左操作数必须是左值,右

           //操作数可以为左值也可以为右值

           M33&operator+=(const M33& m){

                  return*this = *this + m;

           }

           M33&operator-=(const M33& m){

                  return*this = *this - m;

           }

           M33&operator*=(const M33& m){

                  return*this = *this * m;

           }

           constM33 operator-(void)const{

                  returnM33() - *this;

           }

           //前++ --,操作数左值,返回值左值

           M33&operator++(void){

                  for(inti=0; i<3; i++)

                         for(intj=0; j<3; j++)

                                ++m_a[i][j];

                  return*this;

           }

           M33&operator--(void){

                  for(inti=0; i<3; ++i)

                         for(intj=0; j<3; j++)

                                --m_a[i][j];

                  return*this;

           }

           //后++ --,操作符左值,返回值右值

           constM33 operator++(int){

                  M33m = *this;

                  ++*this;

                  returnm;

           }

           constM33 operator--(int){

                  M33m = *this;

                  --*this;

                  returnm;

           }

           int*operator[](int i){

                  returnm_a[i];

           }

           constint* operator[](int i)const{

                  //复用上面的版本

                  returnconst_cast<M33&>(*this) [i];

           }

     

    private:

           intm_a[3][3];

    };

    int main(void)

    {

           inta1[3][3]={1,2,3,4,5,6,7,8,9};

           inta2[3][3]={9,8,7,6,5,4,3,2,1};

           M33m1(a1);

           M33m2(a2);

           cout<< "-m2:" << endl;

           cout<< -m2 << endl;

     

           cout<< "++m2:" << endl;

           cout<< ++m2 << endl;

           cout<< m2 << endl;

           cout<< "--m2:" << endl;

           cout<< --m2 << endl;

           cout<< m2 << endl;

     

           cout<< "m2++:" << endl;

           cout<< m2++ << endl;

           cout<< m2 << endl;

           cout<< "m2--:" << endl;

           cout<< m2-- << endl;

           cout<< m2 << endl;

     

           cout<< "下标:"<< endl;

           for(inti=0;i<3;i++){

                  for(intj=0;j<3;j++)

                         m2[i][j]+= 10;

           }

           cout<< m2 << endl;

           constM33& cm = m2;

           //cm[1][1]= 20;//error

     

           return0;

    }

     

     

    转载请注明原文地址: https://ju.6miu.com/read-1200884.html
    最新回复(0)