• 新标准C++程序设计读书笔记_运算符重载


    形式

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

    运算符重载

    (1)运算符重载的实质是函数重载
    (2)可以重载为普通函数,也可以重载为成员函数

     1 class Complex
     2 {
     3 public:
     4     double real,imag;
     5     Complex( double r = 0.0, double i= 0.0 ):real(r),imag(i) { }
     6     Complex operator-(const Complex & c);
     7 };
     8 
     9 Complex operator+( const Complex & a, const Complex & b)
    10 {
    11     return Complex( a.real + b.real, a.imag + b.imag); //返回一个临时对象
    12 }
    13 
    14 Complex Complex::operator-(const Complex & c)
    15 {
    16     return Complex(real - c.real, imag - c.imag); //返回一个临时对象
    17 }
    18 
    19 int main()
    20 {
    21     Complex a(4,4),b(1,1),c;
    22 
    23     //等价于c=operator+(a,b);
    24     c = a + b; 
    25     cout << c.real << "," << c.imag << endl;
    26 
    27     //a-b等价于a.operator-(b)
    28     cout << (a - b).real << "," << (a - b).imag << endl;
    29     return 0;
    30 }

    (3)把含运算符的表达式转换成对运算符函数的调用
    (4)把运算符的操作数转换成运算符函数的参数
    (5)运算符被多次重载时,根据实参的类型决定调用哪个运算符函数
    (6)重载为成员函数时, 参数个数为运算符目数减一;重载为普通函数时, 参数个数为运算符目数

    赋值运算符 ‘ =’重载

    赋值运算符“ =”只能重载为成员函数

     1 #include <iostream>
     2 
     3 using namespace std;
     4 
     5 class String 
     6 {
     7 private:
     8     char * str;
     9 public:
    10     String ():str(new char[1]) { str[0] = 0;}
    11     const char * c_str() { return str; };
    12     String & operator = (const char * s);
    13     String::~String( ) { delete [] str; }
    14 };
    15 
    16 String & String::operator = (const char * s)
    17 { 
    18     //重载“=”以使得 obj = “hello”能够成立
    19     delete [] str;
    20     str = new char[strlen(s)+1];
    21     strcpy( str, s);
    22     return * this;
    23 }
    24 
    25 int main()
    26 {
    27     String s;
    28     s = "Good Luck," ; //等价于 s.operator=("Good Luck,");
    29     cout << s.c_str() << endl;
    30 
    31     /* 这条语句要是不注释掉就会出错,因为这是一个初始化语句而并不是赋值语句 */
    32     // String s2 = "hello!"; 
    33     
    34     s = "Shenzhou 8!"; //等价于 s.operator=("Shenzhou 8!");
    35     cout << s.c_str() << endl;
    36     return 0;
    37 }

    浅拷贝和深拷贝

    考察下面的代码

    1 String S1, S2;
    2 S1 = “this”;
    3 S2 = “that”;
    4 S1 = S2;

    如果还用上面的运算符重载,那就会出现问题

    (1)如不定义自己的赋值运算符,那么S1=S2实际上导致 S1.str和 S2.str指向同一地方。
    (2)如果S1对象消亡,析构函数将释放S1.str指向的空间,再访问S2的时候就好玩儿了。
    (3)另外,如果执行 S1 = "other";会导致S2.str指向的地方被delete

    因此,要做如下修改:

     1 String & operator = (const String & s)
     2 {
     3     if( this == & s)
     4         return * this;
     5     
     6     delete [] str;
     7     str = new char[strlen(s.str)+1];
     8     strcpy( str,s.str);
     9     return * this;
    10 }

    ps:详细完成一个完整的String类,后续再补充

    运算符重载为友元函数

    一般情况下,将运算符重载为类的成员函数,是较好的选择。但有时,重载为成员函数不能满足使用要求,重载为普通函数,又不能访问类的私有成员,所以需要将运算符重载为友元。

     1 class Complex
     2 {
     3     double real,imag;
     4 public:
     5     Complex( double r, double i):real(r),imag(i){ };
     6     Complex operator+( double r );
     7 };
     8 
     9 Complex Complex::operator+( double r )
    10 { 
    11     //能解释 c+5
    12     return Complex(real + r,imag);
    13 }

    上面的类中重载的加号有局限性

    Complex c ;
    c = c + 5; //有定义,相当于 c = c.operator +(5);
    但是:
    c = 5 + c; //编译出错

    为了使得上述的表达式能成立,需要将 + 重载为普通函数。但是普通函数又不能访问私有成员,所以,需要将运算符 + 重载为友元。

     1 class Complex
     2 {
     3     double real,imag;
     4 public:
     5     Complex( double r, double i):real(r),imag(i){ };
     6     Complex operator+( double r );
     7     friend Complex operator + (double r,const Complex & c);
     8 };
     9 
    10 Complex Complex::operator+( double r )
    11 { 
    12     //能解释 c+5
    13     return Complex(real + r,imag);
    14 }
    15 
    16 Complex operator+ (double r,const Complex & c)
    17 { 
    18     //能解释 5+c
    19     return Complex( c.real + r, c.imag);
    20 }

    设计一个变长数组类

    想要达到下面目的

     1 int main() 
     2 { 
     3     CArray a; //开始里的数组是空的
     4     /*
     5         要用动态分配的内存来存放数组元素,需要一个指针成员变量
     6      */
     7     for( int i = 0;i < 5;++i)
     8         a.push_back(i);
     9 
    10     CArray a2,a3;
    11     /*
    12         要重载“=”
    13      */
    14     a2 = a;
    15     for( int i = 0; i < a.length(); ++i )
    16         /*
    17             要重载“[ ]”
    18          */
    19         cout << a2[i] << " " ;
    20 
    21     a2 = a3; //a2是空的
    22     for( int i = 0; i < a2.length(); ++i ) //a2.length()返回0
    23         cout << a2[i] << " ";
    24     cout << endl;
    25 
    26     a[3] = 100;
    27     /*
    28         要自己写复制构造函数
    29      */
    30     CArray a4(a);
    31     for( int i = 0; i < a4.length(); ++i )
    32         cout << a4[i] << " ";
    33 
    34     return 0;
    35 }

    类的设计如下:

     1 class CArray {
     2     int size; //数组元素的个数
     3     int *ptr; //指向动态分配的数组
     4 public:
     5     CArray(int s = 0); //s代表数组元素的个数
     6     CArray(CArray & a);
     7     ~CArray();
     8     void push_back(int v); //用于在数组尾部添加一个元素v
     9     CArray & operator=( const CArray & a);
    10     //用于数组对象间的赋值
    11     int length() { return size; } //返回数组元素个数
    12     int & CArray::operator[](int i) //返回值为 int 不行!不支持 a[i] = 4
    13     {
    14         //用以支持根据下标访问数组元素,
    15         // 如n = a[i] 和a[i] = 4; 这样的语句
    16         return ptr[i];
    17     }
    18 };
    19 
    20 CArray::CArray(int s):size(s)
    21 {
    22     if( s == 0)
    23         ptr = NULL;
    24     else
    25         ptr = new int[s];
    26 }
    27 
    28 CArray::CArray(CArray & a) 
    29 {
    30     if( !a.ptr) {
    31         ptr = NULL;
    32         size = 0;
    33         return;
    34     }
    35     ptr = new int[a.size];
    36     memcpy( ptr, a.ptr, sizeof(int ) * a.size);
    37     size = a.size;
    38 }
    39 
    40 CArray::~CArray()
    41 {
    42     if( ptr) delete [] ptr;
    43 }
    44 
    45 CArray & CArray::operator=( const CArray & a)
    46 { 
    47     //赋值号的作用是使“=”左边对象里存放的数组,大小和内容都和右边的对象一样
    48     if( ptr == a.ptr) //防止a=a这样的赋值导致出错
    49         return * this;
    50 
    51     if( a.ptr == NULL) { //如果a里面的数组是空的
    52         if( ptr ) 
    53             delete [] ptr;
    54         ptr = NULL;
    55         size = 0;
    56         return * this;
    57     }
    58 
    59     if( size < a.size) { //如果原有空间够大,就不用分配新的空间
    60         if(ptr)
    61             delete [] ptr;
    62         ptr = new int[a.size];
    63     }
    64 
    65     memcpy( ptr, a.ptr, sizeof(int) * a.size);
    66     size = a.size;
    67     return * this;
    68 } 
    69 
    70 void CArray::push_back(int v)
    71 { 
    72     //在数组尾部添加一个元素
    73     if( ptr) {
    74         int * tmpPtr = new int[size + 1]; //重新分配空间
    75         memcpy(tmpPtr, ptr, sizeof(int) * size); //拷贝原数组内容
    76         delete [] ptr;
    77         ptr = tmpPtr;
    78     }
    79     else //数组本来是空的
    80         ptr = new int[1];
    81 
    82     ptr[size++] = v; //加入新的数组元素
    83 }

    流运算符的重载

    假定c是Complex复数类的对象,现在希望写“ cout << c;”,就能以“ a+bi”的形式输出c的值,写“ cin>>c;”,就能从键盘接受“ a+bi”形式的输入,并且使得c.real = a,c.imag = b。

     1 class Complex 
     2 {
     3     double real,imag;
     4 public:
     5     Complex( double r=0, double i=0):real(r),imag(i){ };
     6     friend ostream & operator<<( ostream & os, const Complex & c);
     7     friend istream & operator>>( istream & is,Complex & c);
     8 };
     9 
    10 ostream & operator<<( ostream & os,const Complex & c)
    11 {
    12     os << c.real << "+" << c.imag << "i"; //以"a+bi"的形式输出
    13     return os;
    14 }
    15 
    16 istream & operator>>( istream & is,Complex & c)
    17 {
    18     string s;
    19     is >> s; //将"a+bi"作为字符串读入, “a+bi” 中间不能有空格
    20     int pos = s.find("+", 0);
    21     string sTmp = s.substr(0, pos); //分离出代表实部的字符串
    22     c.real = atof(sTmp.c_str()); //atof库函数能将const char*指针指向的内容转换成 float
    23     sTmp = s.substr(pos+1, s.length()-pos-2); //分离出代表虚部的字符串
    24     c.imag = atof(sTmp.c_str());
    25     return is;
    26 }

    类型转换运算符

    类型强制转换运算符被重载时不能写返回值类型,实际上其返回值类型就是该类型强制转换运算符代表的类型

     1 #include <iostream>
     2 using namespace std;
     3 class Complex
     4 {
     5     double real,imag;
     6 public:
     7     Complex(double r=0,double i=0):real(r),imag(i) { };
     8     //重载强制类型转换运算符 double
     9     operator double () { return real; }
    10 
    11 };
    12 
    13 int main()
    14 {
    15     Complex c(1.2,3.4);
    16     cout << (double)c << endl; //输出 1.2
    17     double n = 2 + c; //等价于 double n=2+c.operator double()
    18     cout << n; //输出 3.2
    19 }

    自增,自减运算符的重载

    1、前置运算符作为一元运算符重载
    (1)重载为成员函数:
    T & operator++();
    T & operator--();
    (2)重载为全局函数:
    T1 & operator++(T2);
    T1 & operator—(T2);

    2、后置运算符作为二元运算符重载,多写一个没用的参数
    (1)重载为成员函数:
    T operator++(int);
    T operator--(int);
    (2)重载为全局函数:
    T1 operator++(T2,int );
    T1 operator—( T2,int);

     1 class CDemo 
     2 {
     3 private:
     4     int n;
     5 public:
     6     CDemo(int i=0):n(i) { }
     7     CDemo & operator++(); //用于前置形式
     8     CDemo operator++( int ); //用于后置形式
     9     operator int ( ) { return n; }
    10     friend CDemo & operator--(CDemo & );
    11     friend CDemo operator--(CDemo & ,int);
    12 };
    13 
    14 CDemo & CDemo::operator++()
    15 { 
    16     //前置 ++
    17     n++;
    18     return * this;
    19 } // ++s即为: s.operator++();
    20 
    21 CDemo CDemo::operator++( int k )
    22 { 
    23     //后置 ++
    24     CDemo tmp(* this); //记录修改前的对象
    25     n++;
    26     return tmp; //返回修改前的对象
    27 } // s++即为: s.operator++(0);
    28 
    29 CDemo & operator--(CDemo & d)
    30 {
    31     //前置--
    32     d.n--;
    33     return d;
    34 } //--s即为: operator--(s);
    35 
    36 CDemo operator--(CDemo & d,int)
    37 {
    38     //后置--
    39     CDemo tmp(d);
    40     d.n--;
    41     return tmp;
    42 } //s--即为: operator--(s, 0);
    43 
    44 int main()
    45 {
    46     CDemo d(5);
    47     cout << (d++ ) << ","; //等价于 d.operator++(0);
    48     cout << d << ",";
    49     cout << (++d) << ","; //等价于 d.operator++();
    50     cout << d << endl;
    51     cout << (d-- ) << ","; //等价于 operator--(d,0);
    52     cout << d << ",";
    53     cout << (--d) << ","; //等价于 operator--(d);
    54     cout << d << endl;
    55     return 0;
    56 }

    运算符重载的注意事项
    (1)C++不允许定义新的运算符 ;
    (2)重载后运算符的含义应该符合日常习惯;
    complex_a + complex_b
    word_a > word_b
    date_b = date_a + n
    (3)运算符重载不改变运算符的优先级;
    (4)以下运算符不能被重载:“.” 、“.*” 、“::” 、“?:” 、 sizeof;
    (5)重载运算符()、 []、 ->或者赋值运算符=时,运算符重载函数必须声明为类的成员函数。

  • 相关阅读:
    docker基础:docker网络模式
    WEB架构师成长之路之一-走正确的路(转载)
    DDD(领域驱动设计)
    C#泛型和泛型约束(转载)
    MES系统介绍
    vue中 computed和watch的一些简单理解(区别)(转载)
    sqlserver常用表值函数
    SQLServerAgent 当前未运行,因此无法将此操作通知它。
    浅谈敏捷开发(转载)
    认证、授权、鉴权和权限控制(转载)
  • 原文地址:https://www.cnblogs.com/abc-begin/p/7779678.html
Copyright © 2020-2023  润新知