精华内容
下载资源
问答
  • C++ 运算符

    2019-09-24 06:57:27
    C++ 运算符运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C++ 内置了丰富的运算符,并提供了以下类型的运算符: 算术运算符关系运算符逻辑运算符运算符赋值运算符杂项运算符本章将逐一介绍算术运算符、...

    C++ 运算符
    运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C++ 内置了丰富的运算符,并提供了以下类型的运算符:

    算术运算符
    关系运算符
    逻辑运算符
    位运算符
    赋值运算符
    杂项运算符
    本章将逐一介绍算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符和其他运算符。

     1 #include <iostream>
     2 #include<iomanip>
     3 /* run this program using the console pauser or add your own getch, system("pause") or input loop */
     4 using namespace std;
     5 int main(int argc, char** argv) {
     6     long f1,f2;
     7     int i;
     8     f1=f2=1;
     9     for(i=1;i<=20;i++){
    10         cout <<setw(12)<<f1<<setw(12)<<f2;
    11         if(i%2==0)cout<<endl;
    12         f1=f1+f2;
    13         f2=f2+f1;
    14     }
    15     return 0;
    16 }

     

    转载于:https://www.cnblogs.com/borter/p/9401007.html

    展开全文
  • C++运算符

    千次阅读 2018-04-15 02:18:15
    C++ 内置了丰富的运算符,并提供了以下类型的运算符:算术运算符关系运算符逻辑运算符运算符赋值运算符杂项运算符本章将逐一介绍算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符和其他运算符。...

    C++ 运算符

    运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C++ 内置了丰富的运算符,并提供了以下类型的运算符:

    • 算术运算符
    • 关系运算符
    • 逻辑运算符
    • 位运算符
    • 赋值运算符
    • 杂项运算符

    本章将逐一介绍算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符和其他运算符。

    算术运算符

    下表显示了 C++ 支持的算术运算符。

    假设变量 A 的值为 10,变量 B 的值为 20,则:

    运算符描述实例
    +把两个操作数相加A + B 将得到 30
    -从第一个操作数中减去第二个操作数A - B 将得到 -10
    *把两个操作数相乘A * B 将得到 200
    /分子除以分母B / A 将得到 2
    %取模运算符,整除后的余数B % A 将得到 0
    ++自增运算符,整数值增加 1A++ 将得到 11
    --自减运算符,整数值减少 1A-- 将得到 9

    实例

    请看下面的实例,了解 C++ 中可用的算术运算符。

    #include <iostream>
    using namespace std;
    
    int main()
    {
    	int a = 21;
    	int b = 10;
    	int c;
    
    	c = a + b;
    	cout << "Line 1 - c 的值是 " << c << endl;
    	c = a - b;
    	cout << "Line 2 - c 的值是 " << c << endl;
    	c = a * b;
    	cout << "Line 3 - c 的值是 " << c << endl;
    	c = a / b;
    	cout << "Line 4 - c 的值是 " << c << endl;
    	c = a % b;
    	cout << "Line 5 - c 的值是 " << c << endl;
    
    	int d = 10;   //  测试自增、自减
    	c = d++;
    	cout << "Line 6 - c 的值是 " << c << endl;
    
    	d = 10;    // 重新赋值
    	c = d--;
    	cout << "Line 7 - c 的值是 " << c << endl;
    	return 0;
    }

    当上面的代码被编译和执行时,它会产生以下结果:

    Line 1 - c 的值是 31
    Line 2 - c 的值是 11
    Line 3 - c 的值是 210
    Line 4 - c 的值是 2
    Line 5 - c 的值是 1
    Line 6 - c 的值是 10
    Line 7 - c 的值是 10

    关系运算符

    下表显示了 C++ 支持的关系运算符。

    假设变量 A 的值为 10,变量 B 的值为 20,则:

    运算符描述实例
    ==检查两个操作数的值是否相等,如果相等则条件为真。(A == B) 不为真。
    !=检查两个操作数的值是否相等,如果不相等则条件为真。(A != B) 为真。
    >检查左操作数的值是否大于右操作数的值,如果是则条件为真。(A > B) 不为真。
    <检查左操作数的值是否小于右操作数的值,如果是则条件为真。(A < B) 为真。
    >=检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。(A >= B) 不为真。
    <=检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。(A <= B) 为真。

    实例

    请看下面的实例,了解 C++ 中可用的关系运算符。

    #include <iostream>
    using namespace std;
     
    int main()
    {
       int a = 21;
       int b = 10;
       int c ;
     
       if( a == b )
       {
          cout << "Line 1 - a 等于 b" << endl ;
       }
       else
       {
          cout << "Line 1 - a 不等于 b" << endl ;
       }
       if ( a < b )
       {
          cout << "Line 2 - a 小于 b" << endl ;
       }
       else
       {
          cout << "Line 2 - a 不小于 b" << endl ;
       }
       if ( a > b )
       {
          cout << "Line 3 - a 大于 b" << endl ;
       }
       else
       {
          cout << "Line 3 - a 不大于 b" << endl ;
       }
       /* 改变 a 和 b 的值 */
       a = 5;
       b = 20;
       if ( a <= b )
       {
          cout << "Line 4 - a 小于或等于 b" << endl ;
       }
       if ( b >= a )
       {
          cout << "Line 5 - b 大于或等于 a" << endl ;
       }
       return 0;
    }

    当上面的代码被编译和执行时,它会产生以下结果:

    Line 1 - a 不等于 b
    Line 2 - a 不小于 b
    Line 3 - a 大于 b
    Line 4 - a 小于或等于 b
    Line 5 - b 大于或等于 a

    逻辑运算符

    下表显示了 C++ 支持的关系逻辑运算符。

    假设变量 A 的值为 1,变量 B 的值为 0,则:

    运算符描述实例
    &&称为逻辑与运算符。如果两个操作数都非零,则条件为真。(A && B) 为假。
    ||称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。(A || B) 为真。
    !称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。!(A && B) 为真。

    实例

    请看下面的实例,了解 C++ 中可用的逻辑运算符。

    #include <iostream>
    using namespace std;
     
    int main()
    {
       int a = 5;
       int b = 20;
       int c ;
     
       if ( a && b )
       {
          cout << "Line 1 - 条件为真"<< endl ;
       }
       if ( a || b )
       {
          cout << "Line 2 - 条件为真"<< endl ;
       }
       /* 改变 a 和 b 的值 */
       a = 0;
       b = 10;
       if ( a && b )
       {
          cout << "Line 3 - 条件为真"<< endl ;
       }
       else
       {
          cout << "Line 4 - 条件不为真"<< endl ;
       }
       if ( !(a && b) )
       {
          cout << "Line 5 - 条件为真"<< endl ;
       }
       return 0;
    }

    当上面的代码被编译和执行时,它会产生以下结果:

    Line 1 - 条件为真
    Line 2 - 条件为真
    Line 4 - 条件不为真
    Line 5 - 条件为真

    位运算符

    位运算符作用于位,并逐位执行操作。&、 | 和 ^ 的真值表如下所示:

    pqp & qp | qp ^ q
    00000
    01011
    11110
    10011

    假设如果 A = 60,且 B = 13,现在以二进制格式表示,它们如下所示:

    A = 0011 1100

    B = 0000 1101

    -----------------

    A&B = 0000 1100

    A|B = 0011 1101

    A^B = 0011 0001

    ~A  = 1100 0011

    下表显示了 C++ 支持的位运算符。假设变量 A 的值为 60,变量 B 的值为 13,则:

    运算符描述实例
    &如果同时存在于两个操作数中,二进制 AND 运算符复制一位到结果中。(A & B) 将得到 12,即为 0000 1100
    |如果存在于任一操作数中,二进制 OR 运算符复制一位到结果中。(A | B) 将得到 61,即为 0011 1101
    ^如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中。(A ^ B) 将得到 49,即为 0011 0001
    ~二进制补码运算符是一元运算符,具有"翻转"位效果,即0变成1,1变成0。(~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。
    <<二进制左移运算符。左操作数的值向左移动右操作数指定的位数。A << 2 将得到 240,即为 1111 0000
    >>二进制右移运算符。左操作数的值向右移动右操作数指定的位数。A >> 2 将得到 15,即为 0000 1111

    实例

    请看下面的实例,了解 C++ 中可用的位运算符。

    #include <iostream>
    using namespace std;
     
    int main()
    {
       unsigned int a = 60;      // 60 = 0011 1100  
       unsigned int b = 13;      // 13 = 0000 1101
       int c = 0;           
     
       c = a & b;             // 12 = 0000 1100
       cout << "Line 1 - c 的值是 " << c << endl ;
     
       c = a | b;             // 61 = 0011 1101
       cout << "Line 2 - c 的值是 " << c << endl ;
     
       c = a ^ b;             // 49 = 0011 0001
       cout << "Line 3 - c 的值是 " << c << endl ;
     
       c = ~a;                // -61 = 1100 0011
       cout << "Line 4 - c 的值是 " << c << endl ;
     
       c = a << 2;            // 240 = 1111 0000
       cout << "Line 5 - c 的值是 " << c << endl ;
     
       c = a >> 2;            // 15 = 0000 1111
       cout << "Line 6 - c 的值是 " << c << endl ;
     
       return 0;
    }

    当上面的代码被编译和执行时,它会产生以下结果:

    Line 1 - c 的值是 12
    Line 2 - c 的值是 61
    Line 3 - c 的值是 49
    Line 4 - c 的值是 -61
    Line 5 - c 的值是 240
    Line 6 - c 的值是 15

    赋值运算符

    下表列出了 C++ 支持的赋值运算符:

    运算符描述实例
    =简单的赋值运算符,把右边操作数的值赋给左边操作数C = A + B 将把 A + B 的值赋给 C
    +=加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数C += A 相当于 C = C + A
    -=减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数C -= A 相当于 C = C - A
    *=乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数C *= A 相当于 C = C * A
    /=除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数C /= A 相当于 C = C / A
    %=求模且赋值运算符,求两个操作数的模赋值给左边操作数C %= A 相当于 C = C % A
    <<=左移且赋值运算符C <<= 2 等同于 C = C << 2
    >>=右移且赋值运算符C >>= 2 等同于 C = C >> 2
    &=按位与且赋值运算符C &= 2 等同于 C = C & 2
    ^=按位异或且赋值运算符C ^= 2 等同于 C = C ^ 2
    |=按位或且赋值运算符C |= 2 等同于 C = C | 2

    实例

    请看下面的实例,了解 C++ 中可用的赋值运算符。

    #include <iostream>
    using namespace std;
     
    int main()
    {
       int a = 21;
       int c ;
     
       c =  a;
       cout << "Line 1 - =  运算符实例,c 的值 = : " <<c<< endl ;
     
       c +=  a;
       cout << "Line 2 - += 运算符实例,c 的值 = : " <<c<< endl ;
     
       c -=  a;
       cout << "Line 3 - -= 运算符实例,c 的值 = : " <<c<< endl ;
     
       c *=  a;
       cout << "Line 4 - *= 运算符实例,c 的值 = : " <<c<< endl ;
     
       c /=  a;
       cout << "Line 5 - /= 运算符实例,c 的值 = : " <<c<< endl ;
     
       c  = 200;
       c %=  a;
       cout << "Line 6 - %= 运算符实例,c 的值 = : " <<c<< endl ;
     
       c <<=  2;
       cout << "Line 7 - <<= 运算符实例,c 的值 = : " <<c<< endl ;
     
       c >>=  2;
       cout << "Line 8 - >>= 运算符实例,c 的值 = : " <<c<< endl ;
     
       c &=  2;
       cout << "Line 9 - &= 运算符实例,c 的值 = : " <<c<< endl ;
     
       c ^=  2;
       cout << "Line 10 - ^= 运算符实例,c 的值 = : " <<c<< endl ;
     
       c |=  2;
       cout << "Line 11 - |= 运算符实例,c 的值 = : " <<c<< endl ;
     
       return 0;
    }

    当上面的代码被编译和执行时,它会产生以下结果:

    Line 1 - =  运算符实例,c 的值 = 21
    Line 2 - += 运算符实例,c 的值 = 42
    Line 3 - -= 运算符实例,c 的值 = 21
    Line 4 - *= 运算符实例,c 的值 = 441
    Line 5 - /= 运算符实例,c 的值 = 21
    Line 6 - %= 运算符实例,c 的值 = 11
    Line 7 - <<= 运算符实例,c 的值 = 44
    Line 8 - >>= 运算符实例,c 的值 = 11
    Line 9 - &= 运算符实例,c 的值 = 2
    Line 10 - ^= 运算符实例,c 的值 = 0
    Line 11 - |= 运算符实例,c 的值 = 2

    杂项运算符

    下表列出了 C++ 支持的其他一些重要的运算符。

    运算符描述
    sizeofsizeof 运算符返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是整数。
    Condition ? X : Y条件运算符。如果 Condition 为真 ? 则值为 X : 否则值为 Y。
    ,逗号运算符会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。
    .(点)和 ->(箭头)成员运算符用于引用类、结构和共用体的成员。
    Cast强制转换运算符把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2。
    &指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。
    *指针运算符 * 指向一个变量。例如,*var; 将指向变量 var。

    C++ 中的运算符优先级

    运算符的优先级确定表达式中项的组合。这会影响到一个表达式如何计算。某些运算符比其他运算符有更高的优先级,例如,乘除运算符具有比加减运算符更高的优先级。

    例如 x = 7 + 3 * 2,在这里,x 被赋值为 13,而不是 20,因为运算符 * 具有比 + 更高的优先级,所以首先计算乘法 3*2,然后再加上 7。

    下表将按运算符优先级从高到低列出各个运算符,具有较高优先级的运算符出现在表格的上面,具有较低优先级的运算符出现在表格的下面。在表达式中,较高优先级的运算符会优先被计算。

    类别 运算符 结合性 
    后缀 () [] -> . ++ - -  从左到右 
    一元 + - ! ~ ++ - - (type)* & sizeof 从右到左 
    乘除 * / % 从左到右 
    加减 + - 从左到右 
    移位 << >> 从左到右 
    关系 < <= > >= 从左到右 
    相等 == != 从左到右 
    位与 AND 从左到右 
    位异或 XOR 从左到右 
    位或 OR 从左到右 
    逻辑与 AND && 从左到右 
    逻辑或 OR || 从左到右 
    条件 ?: 从右到左 
    赋值 = += -= *= /= %=>>= <<= &= ^= |= 从右到左 
    逗号 从左到右 

    实例

    请看下面的实例,了解 C++ 中运算符的优先级。

    对比有括号和没有括号时的区别,这将产生不同的结果。因为 ()、 /、 * 和 + 有不同的优先级,高优先级的操作符将优先计算。

    #include <iostream>
    using namespace std;
     
    int main()
    {
       int a = 20;
       int b = 10;
       int c = 15;
       int d = 5;
       int e;
     
       e = (a + b) * c / d;      // ( 30 * 15 ) / 5
       cout << "(a + b) * c / d 的值是 " << e << endl ;
     
       e = ((a + b) * c) / d;    // (30 * 15 ) / 5
       cout << "((a + b) * c) / d 的值是 " << e << endl ;
     
       e = (a + b) * (c / d);   // (30) * (15/5)
       cout << "(a + b) * (c / d) 的值是 " << e << endl ;
     
       e = a + (b * c) / d;     //  20 + (150/5)
       cout << "a + (b * c) / d 的值是 " << e << endl ;
      
       return 0;
    }

    当上面的代码被编译和执行时,它会产生以下结果:

    (a + b) * c / d 的值是 90
    ((a + b) * c) / d 的值是 90
    (a + b) * (c / d) 的值是 90
    a + (b * c) / d 的值是 50
    展开全文
  • c++运算符

    2018-09-18 11:08:19
    c++中,运算符分为一元运算符和二元运算符, 一元运算符:作用于一个运算对象的运算符,如取地址符(&amp;)和解引用符(*) 二元运算符:作用于两个运算对象的运算符,如==和乘法运算符 *, 三元运算符:?,...

    运算符
    在c++中,运算符分为一元运算符和二元运算符,
    一元运算符:作用于一个运算对象的运算符,如取地址符(&)和解引用符(*)
    二元运算符:作用于两个运算对象的运算符,如==和乘法运算符 *,
    三元运算符:?,特殊的运算符
    此外函数调用也算一种特殊的运算符,对运算对象的数量没有限制。
    左值和右值
    对于左值和右值,左值表达式的结果是一个对象或者函数,而常量对象实际上不能作为赋值语句的左侧运算对象,此外,虽然某些表达式的求值结果是对象,但它们确实右值而非左值,简单的归纳就是:当一个对象被用作右值的时候,用的是对象的内容,而当对象被用作左值的时候,用的是对象的身份(内存中的位置)。
    优先级和结合律
    复合表达式指含有两个或多个运算符的表达式,求复合表达式的值需要首先将运算符和运算对象合理的组合。运算优先级高的先结合,运算优先级相同的按照从左到右的顺序结合。加上括号时无视运算符优先级。
    递增和递减运算符
    递增和递减运算符有两种形式,前置和后置版本
    前置版:先将运算对象加1,后作为求值结果。
    后置版:先求值,后加1。
    前置版为引用,效率更加高,后置版需先将原始值存储为临时对象以便返回这个未修改的内容。所以尽量的使用前置版本的递增符号。

    展开全文
  • c++运算符重载,重载运算符
  • C++ 运算符重载

    万次阅读 多人点赞 2018-12-26 21:39:21
    1. C++运算符重载的概念和原理 2. C++重载=(C++重载赋值运算符) 3. C++深拷贝和浅拷贝(C++深复制和浅复制) 4. C++运算符重载为友元函数 5. C++实现可变长度的动态数组 6. C++重载&lt;&lt;和&gt;...

    前言

    本文引用于“C语言中文网”,我整理出来放在博客,方便大家共同学习。所有知识点和代码均已亲测可用,如有疑问,可提出,一起讨论学习。

    本章内容:

    1. C++运算符重载的概念和原理
    2. C++重载=(C++重载赋值运算符)
    3. C++深拷贝和浅拷贝(C++深复制和浅复制)
    4. C++运算符重载为友元函数
    5. C++实现可变长度的动态数组
    6. C++重载<<和>>(C++重载输出运算符和输入运算符)
    7. C++重载()(强制类型转换运算符)
    8. C++重载++和--(自增和自减运算符)
    9. C++运算符重载注意事项以及汇总

    1 C++运算符重载的概念和原理 

    如果不做特殊处理,C++ 的 +、-、*、/ 等运算符只能用于对基本类型的常量或变量进行运算,不能用于对象之间的运算。

    有时希望对象之间也能用这些运算符进行运算,以达到使程序更简洁、易懂的目的。例如,复数是可以进行四则运算的,两个复数对象相加如果能直接用+运算符完成,不是很直观和简洁吗?

    利用 C++ 提供的“运算符重载”机制,赋予运算符新的功能,就能解决用+将两个复数对象相加这样的问题。

    运算符重载,就是对已有的运算符赋予多重含义,使同一运算符作用于不同类型的数据时产生不同的行为。运算符重载的目的是使得 C++ 中的运算符也能够用来操作对象。

    运算符重载的实质是编写以运算符作为名称的函数。不妨把这样的函数称为运算符函数。运算符函数的格式如下:

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

    包含被重载的运算符的表达式会被编译成对运算符函数的调用,运算符的操作数成为函数调用时的实参,运算的结果就是函数的返回值。运算符可以被多次重载。

    运算符可以被重载为全局函数,也可以被重载为成员函数。一般来说,倾向于将运算符重载为成员函数,这样能够较好地体现运算符和类的关系。来看下面的例子:

    #include <iostream>
    using namespace std;
    class Complex
    {
        public:
        double real, imag;
        Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) { }
        Complex operator - (const Complex & c);
    };
    Complex operator + (const Complex & a, const Complex & b)
    {
        return Complex(a.real + b.real, a.imag + b.imag); //返回一个临时对象
    }
    Complex Complex::operator - (const Complex & c)
    {
        return Complex(real - c.real, imag - c.imag); //返回一个临时对象
    }
    int main()
    {
        Complex a(4, 4), b(1, 1), c;
        c = a + b; //等价于 c = operator + (a,b);
        cout << c.real << "," << c.imag << endl;
        cout << (a - b).real << "," << (a - b).imag << endl; //a-b等价于a.operator - (b)
        return 0;
    }

    程序的输出结果是:

    5,5
    3,3

    程序将+重载为一个全局函数(只是为了演示这种做法,否则重载为成员函数更好),将-重载为一个成员函数。

    运算符重载为全局函数时,参数的个数等于运算符的目数(即操作数的个数);运算符重载为成员函数时,参数的个数等于运算符的目数减一。

    如果+没有被重载,第 21 行会编译出错,因为编译器不知道如何对两个 Complex 对象进行+运算。有了对+的重载,编译器就将a+b理解为对运算符函数的调用,即operator+(a,b),因此第 21 行就等价于:

    c = operator+(a, b);

    即以两个操作数 a、b 作为参数调用名为operator+的函数,并将返回值赋值给 c。

    第 12 行,在 C++ 中,“类名(构造函数实参表)”这种写法表示生成一个临时对象。该临时对象没有名字,生存期就到包含它的语句执行完为止。因此,第 12 行实际上生成了一个临时的 Complex 对象作为 return 语句的返回值,该临时对象被初始化为 a、b 之和。第 16 行与第 12 行类似。

    由于-被重载为 Complex 类的成员函数,因此,第 23 行中的a-b就被编译器处理成:

    a.operator-(b);

    由此就能看出,为什么运算符重载为成员函数时,参数个数要比运算符目数少 1 了。

    2 C++重载=(C++重载赋值运算符)

    赋值运算符=要求左右两个操作数的类型是匹配的,或至少是兼容的。有时希望=两边的操作数的类型即使不兼容也能够成立,这就需要对=进行重载。C++ 规定,=只能重载为成员函数。来看下面的例子。

    要编写一个长度可变的字符串类 String,该类有一个 char* 类型的成员变量,用以指向动态分配的存储空间,该存储空间用来存放以\0结尾的字符串。String 类可以如下编写:

    #include <iostream>
    #include <cstring>
    using namespace std;
    class String {
    private:
        char * str;
    public:
        String() :str(NULL) { }
        const char * c_str() const { return str; };
        String & operator = (const char * s);
        ~String();
    };
    String & String::operator = (const char * s)
    //重载"="以使得 obj = "hello"能够成立
    {
        if (str)
            delete[] str;
        if (s) { //s不为NULL才会执行拷贝
            str = new char[strlen(s) + 1];
            strcpy(str, s);
        }
        else
            str = NULL;
        return *this;
    }
    String::~String()
    {
        if (str)
            delete[] str;
    };
    int main()
    {
        String s;
        s = "Good Luck,"; //等价于 s.operator=("Good Luck,");
        cout << s.c_str() << endl;
        // String s2 = "hello!"; //这条语句要是不注释掉就会出错
        s = "Shenzhou 8!"; //等价于 s.operator=("Shenzhou 8!");
        cout << s.c_str() << endl;
        return 0;
    }

    程序的运行结果:

    Good Luck,
    Shenzhou 8!

    第 8 行的构造函数将 str 初始化为 NULL,仅当执行了 operator= 成员函数后,str 才会指向动态分配的存储空间,并且从此后其值不可能再为 NULL。在 String 对象的生存期内,有可能从未执行过 operator= 成员函数,所以在析构函数中,在执行delete[] str之前,要先判断 str 是否为 NULL。

    第 9 行的函数返回了指向 String 对象内部动态分配的存储空间的指针,但是不希望外部得到这个指针后修改其指向的字符串的内容,因此将返回值设为 const char*。这样,假定 s 是 String 对象,那么下面两条语句编译时都会报错,s 对象内部的字符串就不会轻易地从外部被修改了 :

    char* p = s.c_str ();
    strcpy(s.c_str(), "Tiangong1");

    第一条语句出错是因为=左边是 char* 类型,右边是 const char * 类型,两边类型不匹配;第二条语句出错是因为 strcpy 函数的第一个形参是 char* 类型,而这里实参给出的却是 const char * 类型,同样类型不匹配。

    如果没有第 13 行对=的重载,第 34 行的s = "Good Luck,"肯定会因为类型不匹配而编译出错。经过重载后,第 34 行等价s.operator=("Good Luck,");,就没有问题了。

    在 operator= 函数中,要先判断 str 是否已经指向动态分配的存储空间,如果是,则要先释放那片空间,然后重新分配一片空间,再将参数 s 指向的内容复制过去。这样,对象中存放的字符串就和 s 指向的字符串一样了。分配空间时,要考虑到字符串结尾的\0,因此分配的字节数要比 strlen(s) 多 1。

    需要注意一点,即使对=做了重载,第 36 行的String s2 = "hello!";还是会编译出错,因为这是一条初始化语句,要用到构造函数,而不是赋值运算符=。String 类没有编写参数类型为 char * 的构造函数,因此编译不能通过。

    就上面的程序而言,对 operator= 函数的返回值类型没有什么特别要求,void 也可以。但是在对运算符进行重载时,好的风格是应该尽量保留运算符原本的特性,这样其他人在使用这个运算符时才不容易产生困惑。赋值运算符是可以连用的,这个特性在重载后也应该保持。即下面的写法应该合法:

    a = b = c;

    假定 a、b、c 都是 String 对象,则上面的语句等价于下面的嵌套函数调用:

    a.operator=( b.operator=(c) );

    如果 operator= 函数的返回值类型为 void,显然上面这个嵌套函数调用就不能成立。将返回值类型改为 String 并且返回 *this 可以解决问题,但是还不够好。因为,假设 a、b、c 是基本类型的变量,则

    (a =b) = c;

    这条语句执行的效果会使得 a 的值和 c 相等,即a = b这个表达式的值其实是 a 的引用。为了保持=的这个特性,operator= 函数也应该返回其所作用的对象的引用。因此,返回值类型为 String & 才是风格最好的写法。在 a、b、c 都是 String 对象时,(a=b)=c;等价于

    ( a.operator=(b) ).operator=(c);

    a.operator=(b) 返回对 a 的引用后,通过该引用继续调用 operator=(c),就会改变 a 的值。

    3 C++深拷贝和浅拷贝(C++深复制和浅复制) 

    同类对象之间可以通过赋值运算符=互相赋值。如果没有经过重载,=的作用就是把左边的对象的每个成员变量都变得和右边的对象相等,即执行逐个字节拷贝的工作,这种拷贝叫作“浅拷贝”。

    有的时候,两个对象相等,从实际应用的含义上来讲,指的并不应该是两个对象的每个字节都相同,而是有其他解释,这时就需要对=进行重载。

    上节我们定义了 String 类,并重载了=运算符,使得 char * 类型的字符串可以赋值给 String 类的对象。完整代码如下:

    #include <iostream>
    #include <cstring>
    using namespace std;
    class String {
    private:
        char * str;
    public:
        String() :str(NULL) { }
        const char * c_str() const { return str; };
        String & operator = (const char * s);
        ~String();
    };
    String & String::operator = (const char * s)
    //重载"="以使得 obj = "hello"能够成立
    {
        if (str)
            delete[] str;
        if (s) { //s不为NULL才会执行拷贝
            str = new char[strlen(s) + 1];
            strcpy(str, s);
        }
        else
            str = NULL;
        return *this;
    }
    String::~String()
    {
        if (str)
            delete[] str;
    };
    int main()
    {
        String s;
        s = "Good Luck,"; //等价于 s.operator=("Good Luck,");
        cout << s.c_str() << endl;
        // String s2 = "hello!"; //这条语句要是不注释掉就会出错
        s = "Shenzhou 8!"; //等价于 s.operator=("Shenzhou 8!");
        cout << s.c_str() << endl;
        return 0;
    }

    对于上面的代码,如果让两个 String 对象相等(把一个对象赋值给另一个对象),其意义到底应该是什么呢?是两个对象的 str 成员变量都指向同一个地方,还是两个对象的 str 成员变量指向的内存空间中存放的内容相同?如果把 String 对象理解为存放字符串的对象,那应该是后者比较合理和符合习惯,而前者不但不符合习惯,还会导致程序漏洞。

    按照上面代码中 String 类的写法,下面的程序片段会引发问题:

    String s1, s2;
    s1 = "this";
    s2 = "that";
    s2 = s1;

    执行完上面的第 3 行后,s1 和 s2 的状态如图 1 (a) 所示,它们的 str 成员变量指向不同的存储空间。
     


                                                                                   图1:浅拷贝导致的错误

    s2=s1;执行的是浅拷贝。执行完s2=s1;后,s2.str s1.str 指向同一个地方, 如图 1 (b) 所示。这导致 s2.str 原来指向的那片动态分配的存储空间再也不会被释放,变成内存垃圾。

    此外,s1 和 s2 消亡时都会执行delete[] str;,这就使得同一片存储空间被释放两次,会导致严重的内存错误,可能引发程序意外中止。

    而且,如果执行完s1=s2;后 又执行s1 = "some";,则会导致 s2.str 也被释放。

    为解决上述问题,需要对做=再次重载。重载后的的逻辑,应该是使得执行s2=s1;后,s2.str 和 s1.str 依然指向不同的地方,但是这两处地方所存储的字符串是一样的。再次重载=的写法如下:

    String & String::operator = (const String & s)
    {
        if(str == s.str)
            return * this;
        if(str)
            delete[] str;
        if(s.str){ //s. str不为NULL才执行复制操作
            str = new char[ strlen(s.str) + 1 ];
            strcpy(str, s.str);
        }
        else
            str = NULL;
        return * this;
    }

    经过重载,赋值号=的功能不再是浅拷贝,而是将一个对象中指针成员变量指向的内容复制到另一个对象中指针成员变量指向的地方。这样的拷贝就叫“深拷贝”。

    程序第 3 行要判断 str==s.str,是因为要应付如下的语句:

    s1 = s1;

    这条语句本该不改变s1的值才对。s1=s1;等价于s.operator=(s1);,如果没有第 3 行和第 4 行,就会导致函数执行中的 str 和 s.str 完全是同一个指针(因为形参 s 引用了实参 s1,因此可以说 s 就是 s1)。第 8 行为 str 新分配一片存储空间,第 9 行从自己复制到自己,那么 str 指向的内容就不知道变成什么了。

    当然,程序员可能不会写s1=s1;这样莫名奇妙的语句,但是可能会写rs1=rs2;,如果 rs1 和 rs2 都是 String 类的引用,而且它们正好引用了同一个 String 对象,那么就等于发生了s1=s1;这样的情况。

    思考题:上面的两个 operator= 函数有什么可以改进以提高执行效率的地方?

    重载了两次=的 String 类依然可能导致问题。因为没有编写复制构造函数,所以一旦出现使用复制构造函数初始化的 String 对象(例如,String 对象作为函数形参,或 String 对象作为函数返回值),就可能导致问题。最简单的可能出现问题的情况如下:

    String s2;
    s2 = "Transformers";
    String s1(s2);

    s1 是以 s2 作为实参,调用默认复制构造函数来初始化的。默认复制构造函数使得 s1.str 和 s2.str 指向同一个地方,即执行的是浅拷贝,这就导致了前面提到的没有对=进行第二次重载时产生的问题。因此还应该为 String 类编写如下复制构造函数,以完成深拷贝:

    String::String(String & s)
    {
        if(s.str){
            str = new char[ strlen(s.str) + 1 ];
            strcpy(str, s.str);
        }
        else
            str = NULL;
    }
    
    最后,给出 String 类的完整代码:
    class String {
    private:
        char * str;
    public:
        String() :str(NULL) { }
        String(String & s);
        const char * c_str() const { return str; };
        String & operator = (const char * s);
        String & operator = (const String & s);
        ~String();
    };
    String::String(String & s)
    {
        if (s.str) {
            str = new char[strlen(s.str) + 1];
            strcpy(str, s.str);
        }
        else
            str = NULL;
    }
    String & String::operator = (const String & s)
    {
        if (str == s.str)
            return *this;
        if (str)
            delete[] str;
        if (s.str) { //s. str不为NULL才执行复制操作
            str = new char[strlen(s.str) + 1];
            strcpy(str, s.str);
        }
        else
            str = NULL;
        return *this;
    }
    String & String::operator = (const char * s)
    //重载"="以使得 obj = "hello"能够成立
    {
        if (str)
            delete[] str;
        if (s) { //s不为NULL才会执行拷贝
            str = new char[strlen(s) + 1];
            strcpy(str, s);
        }
        else
            str = NULL;
        return *this;
    }
    String::~String()
    {
        if (str)
            delete[] str;
    };

    4 C++运算符重载为友元函数

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

    例如,对于复数类 Complex 的对象,希望它能够和整型以及实数型数据做四则运算,假设 c 是 Complex 对象,希望c+55+c这两个表达式都能解释得通。

    将+重载为 Complex 类的成员函数能解释c+5,但是无法解释5+c。要让5+c有意义,则应对+进行再次重载,将其重载为一个全局函数。为了使该全局函数能访问 Complex 对象的私有成员,就应该将其声明为 Complex 类的友元。具体写法如下:

    class Complex
    {
        double real, imag;
    public:
        Complex(double r, double i):real(r), imag(i){};
        Complex operator + (double r);
        friend Complex operator + (double r, const Complex & c);
    };
    Complex Complex::operator + (double r)
    { //能解释c+5
        return Complex(real+r, imag);
    }
    Complex operator + (double r, const Complex & c)
    { //能解释5+c
        return Complex (c.real+r, c.imag);
    }

     5 C++实现可变长度的动态数组

    实践中经常碰到程序需要定义一个数组,但不知道定义多大合适的问题。按照最大的可能性定义,会造成空间浪费;定义小了则无法满足需要。

    如果用动态内存分配的方式解决,需要多少空间就动态分配多少,固然可以解决这个问题,但是要确保动态分配的内存在每一条执行路径上都能够被释放,也是一件头疼的事情。

    因此需要编写一个长度可变的数组类,该类的对象就能存放一个可变长数组。该数组类应该有以下特点:

    • 数组的元素个数可以在初始化该对象时指定。
    • 可以动态往数组中添加元素。
    • 使用该类时不用担心动态内存分配和释放问题。
    • 能够像使用数组那样使用动态数组类对象,如可以通过下标访问其元素。

    程序代码如下:

    #include <iostream>
    #include <cstring>
    using namespace std;
    class CArray
    {
    	int size; //数组元素的个数
    	int* ptr; //指向动态分配的数组
    public:
    	CArray(int s = 0); //s代表数组元素的个数
    	CArray(CArray & a);
    	~CArray();
    	void push_back(int v); //用于在数组尾部添加一个元素 v
    	CArray & operator = (const CArray & a); //用于数组对象间的赋值
    	int length() const { return size; } //返回数组元素个数
    	int & operator[](int i)
    	{ //用以支持根据下标访问数组元素,如“a[i]=4;”和“n=a[i];”这样的语句
    		return ptr[i];
    	};
    };
    CArray::CArray(int s) : size(s)
    {
    	if (s == 0)
    		ptr = NULL;
    	else
    		ptr = new int[s];
    }
    CArray::CArray(CArray & a)
    {
    	if (!a.ptr) {
    		ptr = NULL;
    		size = 0;
    		return;
    	}
    	ptr = new int[a.size];
    	memcpy(ptr, a.ptr, sizeof(int) * a.size);
    	size = a.size;
    }
    CArray::~CArray()
    {
    	if (ptr) delete[] ptr;
    }
    CArray & CArray::operator=(const CArray & a)
    { //赋值号的作用是使 = 左边对象中存放的数组的大小和内容都与右边的对象一样
    	if (ptr == a.ptr) //防止 a=a 这样的赋值导致出错
    		return *this;
    	if (a.ptr == NULL) { //如果a里面的数组是空的
    		if (ptr)
    		delete[] ptr;
    		ptr = NULL;
    		size = 0;
    		return *this;
    	}
    	if (size < a.size) { //如果原有空间够大,就不用分配新的空间
    		if (ptr)
    			delete[] ptr;
    		ptr = new int[a.size];
    	}
    	memcpy(ptr, a.ptr, sizeof(int)*a.size);
    	size = a.size;
    	return *this;
    }
    void CArray::push_back(int v)
    { //在数组尾部添加一个元素
    	if (ptr) {
    		int* tmpPtr = new int[size + 1]; //重新分配空间
    		memcpy(tmpPtr, ptr, sizeof(int) * size); //复制原数组内容
    		delete[] ptr;
    		ptr = tmpPtr;
    	}
    	else //数组本来是空的
    		ptr = new int[1];
    	ptr[size++] = v; //加入新的数组元素
    }
    int main()
    {
    	CArray a; //开始的数组是空的
    	for (int i = 0; i<5; ++i)
    		a.push_back(i);
    	CArray a2, a3;
    	a2 = a;
    	for (int i = 0; i<a.length(); ++i)
    		cout << a2[i] << " ";
    	a2 = a3; //a2 是空的
    	for (int i = 0; i<a2.length(); ++i) //a2.length()返回 0
    		cout << a2[i] << " ";
    	cout << endl;
    	a[3] = 100;
    	CArray a4(a);
    	for (int i = 0; i<a4.length(); ++i)
    		cout << a4[i] << " ";
    	return 0;
    }

    程序的输出结果为:

    0 1 2 3 4
    0 1 2 100 4

    []是双目运算符,有两个操作数,一个在里面,一个在外面。表达式 a[i] 等价于 a.operator[](i)。按照[]原有的特性,a[i]应该能够作为左值使用,因此 operator[] 函数应该返回引用。

    思考题:每次在数组尾部添加一个元素都要重新分配内存并且复制原有内容,显然效率是低下的。有什么办法能够加快添加元素的速度呢?

    6 C++重载<<和>>(C++重载输出运算符和输入运算符)

    在 C++ 中,左移运算符<<可以和 cout 一起用于输出,因此也常被称为“流插入运算符”或者“输出运算符”。实际上,<<本来没有这样的功能,之所以能和 cout 一起使用,是因为被重载了。

    cout 是 ostream 类的对象。ostream 类和 cout 都是在头文件 <iostream> 中声明的。ostream 类将<<重载为成员函数,而且重载了多次。为了使cout<<"Star War"能够成立,ostream 类需要将<<进行如下重载:

    ostream & ostream::operator << (const char* s)
    {
        //输出s的代码
        return * this;
    }

    为了使cout<<5;能够成立,ostream 类还需要将<<进行如下重载:

    ostream & ostream::operator << (int n)
    {
        //输出n的代码
        return *this;
    }

    重载函数的返回值类型为 ostream 的引用,并且函数返回 *this,就使得cout<<"Star War"<<5能够成立。有了上面的重载,cout<<"Star War"<<5;就等价于:

    ( cout.operator<<("Star War") ).operator<<(5);

    重载函数返回 *this,使得cout<<"Star War"这个表达式的值依然是 cout(说得更准确一点就是 cout 的引用,等价于 cout),所以能够和<<5继续进行运算。

    cin 是 istream 类的对象,是在头文件 <iostream> 中声明的。istream 类将>>重载为成员函数,因此 cin 才能和>>连用以输入数据。一般也将>>称为“流提取运算符”或者“输入运算符”。

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

    显然,要对<<>>进行重载,程序如下:

    #include <iostream>
    #include <string>
    #include <cstdlib>
    using namespace std;
    class Complex
    {
    	double real,imag;
    public:
    	Complex( double r=0, double i=0):real(r),imag(i){ };
    	friend ostream & operator<<( ostream & os,const Complex & c);
    	friend istream & operator>>( istream & is,Complex & c);
    };
    ostream & operator<<( ostream & os,const Complex & c)
    {
    	os << c.real << "+" << c.imag << "i"; //以"a+bi"的形式输出
    	return os;
    }
    istream & operator>>( istream & is,Complex & c)
    {
    	string s;
    	is >> s; //将"a+bi"作为字符串读入, "a+bi" 中间不能有空格
    	int pos = s.find("+",0);
    	string sTmp = s.substr(0,pos); //分离出代表实部的字符串
    	c.real = atof(sTmp.c_str());//atof库函数能将const char*指针指向的内容转换成 float
    	sTmp = s.substr(pos+1, s.length()-pos-2); //分离出代表虚部的字符串
    	c.imag = atof(sTmp.c_str());
    	return is;
    }
    int main()
    {
    	Complex c;
    	int n;
    	cin >> c >> n;
    	cout << c << "," << n;
    	return 0;
    }

    程序的运行结果:

    13.2+133i 87
    13.2+133i,87

    因为没有办法修改 ostream 类和 istream 类,所以只能将<<>>重载为全局函数的形式。由于这两个函数需要访问 Complex 类的私有成员,因此在 Complex 类定义中将它们声明为友元。

    cout<<c会被解释成operator<<(cout, c),因此编写 operator<< 函数时,它的两个参数就不难确定了。

    第 13 行,参数 os 只能是 ostream 的引用,而不能是 ostream 对象,因为 ostream 的复制构造函数是私有的,没有办法生成 ostream 参数对象。operator<< 函数的返回值类型设为 ostream &,并且返回 os,就能够实现<<的连续使用,如cout<<c<<5。在本程序中,执行第 34 行的cout<<c进入 operator<< 后,os 引用的就是 cout,因此第 34 行就能产生输出。

    用 cin 读入复数时,对应的输入必须是 a+bi 的格式,而且中间不能有空格,如输入 13.2+33.4i。第 21 行的is>>s;读入一个字符串。假定输入的格式没有错误,那么被读入 s  的就是 a+bi 格式的字符串。

    读入后需要将字符串中的实部 a 和虚部 b 分离出来,分离的办法就是找出被+隔开的两个子串,然后将两个字符串转换成浮点数。第 24 行调用了标准库函数 atof 来将字符串转换为浮点数。该函数的原型是float atof(const char *),它在 <cstdlib> 头文件中声明。

    7 C++重载()(强制类型转换运算符)

    在 C++ 中,类型的名字(包括类的名字)本身也是一种运算符,即类型强制转换运算符。

    类型强制转换运算符是单目运算符,也可以被重载,但只能重载为成员函数,不能重载为全局函数。经过适当重载后,(类型名)对象这个对对象进行强制类型转换的表达式就等价于对象.operator 类型名(),即变成对运算符函数的调用。

    下面的程序对 double 类型强制转换运算符进行了重载。

    #include <iostream>
    using namespace std;
    class Complex
    {
        double real, imag;
    public:
        Complex(double r = 0, double i = 0) :real(r), imag(i) {};
        operator double() { return real; } //重载强制类型转换运算符 double
    };
    int main()
    {
        Complex c(1.2, 3.4);
        cout << (double)c << endl; //输出 1.2
        double n = 2 + c; //等价于 double n = 2 + c. operator double()
        cout << n; //输出 3.2
    }

    程序的输出结果是:

    1.2
    3.2

    第 8 行对 double 运算符进行了重载。重载强制类型转换运算符时,不需要指定返回值类型,因为返回值类型是确定的,就是运算符本身代表的类型,在这里就是 double。

    重载后的效果是,第 13 行的(double)c等价于c.operator double()

    有了对 double 运算符的重载,在本该出现 double 类型的变量或常量的地方,如果出现了一个 Complex 类型的对象,那么该对象的 operator double 成员函数就会被调用,然后取其返回值使用。

    例如第 14 行,编译器认为本行中c这个位置如果出现的是 double 类型的数据,就能够解释得通,而 Complex 类正好重载了 double 运算符,因而本行就等价于:

    double n = 2 + c.operator double();

    8 C++重载++和--(自增和自减运算符)

    自增运算符++、自减运算符--都可以被重载,但是它们有前置、后置之分。

    ++为例,假设 obj 是一个 CDemo 类的对象,++objobj++本应该是不一样的,前者的返回值应该是 obj 被修改后的值,而后者的返回值应该是 obj 被修改前的值。如果如下重载++运算符:

    CDemo & CDemo::operator ++ ()
    {
        //...
        return * this;
    }

    那么不论obj++还是++obj,都等价于obj.operator++()无法体现出差别。

    为了解决这个问题,C++ 规定,在重载++--时,允许写一个增加了无用 int 类型形参的版本,编译器处理++--前置的表达式时,调用参数个数正常的重载函数;处理后置表达式时,调用多出一个参数的重载函数。来看下面的例子:

    #include <iostream>
    using namespace std;
    class CDemo {
    private:
    	int n;
    public:
    	CDemo(int i=0):n(i) { }
    	CDemo & operator++(); //用于前置形式
    	CDemo operator++( int ); //用于后置形式
    	operator int ( ) { return n; }
    	friend CDemo & operator--(CDemo & );
    	friend CDemo operator--(CDemo & ,int);
    };
    CDemo & CDemo::operator++()
    {//前置 ++
    	n ++;
    	return * this;
    }
    CDemo CDemo::operator++(int k )
    { //后置 ++
    	CDemo tmp(*this); //记录修改前的对象
    	n++;
    	return tmp; //返回修改前的对象
    }
    CDemo & operator--(CDemo & d)
    {//前置--
    	d.n--;
    	return d;
    }
    CDemo operator--(CDemo & d,int)
    {//后置--
    	CDemo tmp(d);
    	d.n --;
    	return tmp;
    }
    int main()
    {
    	CDemo d(5);
    	cout << (d++ ) << ","; //等价于 d.operator++(0);
    	cout << d << ",";
    	cout << (++d) << ","; //等价于 d.operator++();
    	cout << d << endl;
    	cout << (d-- ) << ","; //等价于 operator-(d,0);
    	cout << d << ",";
    	cout << (--d) << ","; //等价于 operator-(d);
    	cout << d << endl;
    	return 0;
    }

    程序运行结果:

    5,6,7,7
    7,6,5,5

    本程序将++重载为成员函数,将--重载为全局函数。其实都重载为成员函数更好,这里将--重载为全局函数只是为了说明可以这么做而已。

    调用后置形式的重载函数时,对于那个没用的 int 类型形参,编译器自动以 0 作为实参。 如第 39 行,d++等价于d.operator++(0)

    对比前置++和后置++运算符的重载可以发现,后置++运算符的执行效率比前置的低。因为后置方式的重载函数中要多生成一个局部对象 tmp(第21行),而对象的生成会引发构造函数调用,需要耗费时间。同理,后置--运算符的执行效率也比前置的低。

    前置++运算符的返回值类型是 CDemo &,而后置++运算符的返回值类型是 CDemo,这是因为运算符重载最好保持原运算符的用法。C++ 固有的前置++运算符的返回值本来就是操作数的引用,而后置++运算符的返回值则是操作数值修改前的复制品。例如:

    int a = 5;
    (++a) = 2;

    上面两条语句执行后,a 的值是 2,因为 ++a 的返回值是 a 的引用。而

    (a++) = 2;

    这条语句是非法的,因为 a++ 的返回值不是引用,不能作为左值。

    --运算符的返回值类型的设定和++运算符一样。

    在有的编译器(如Visual Studio)中,如果没有后置形式的重载,则后置形式的自增或自减表达式也被当作前置形式处理。而在有的编译器(如Dev C++)中,不进行后置形式的重载,则后置形式的表达式就会编译出错。

    9 C++运算符重载注意事项以及汇总

    在 C++ 中进行运算符重载时,有以下问题需要注意:

    • 重载后运算符的含义应该符合原有用法习惯。例如重载+运算符,完成的功能就应该类似于做加法,在重载的+运算符中做减法是不合适的。此外,重载应尽量保留运算符原有的特性。
    • C++ 规定,运算符重载不改变运算符的优先级。
    • 以下运算符不能被重载:..*::? :sizeof
    • 重载运算符()[]->、或者赋值运算符=时,只能将它们重载为成员函数,不能重载为全局函数。

    运算符重载的实质是将运算符重载为一个函数,使用运算符的表达式就被解释为对重载函数的调用。

    运算符可以重载为全局函数。此时函数的参数个数就是运算符的操作数个数,运算符的操作数就成为函数的实参。

    运算符也可以重载为成员函数。此时函数的参数个数就是运算符的操作数个数减一,运算符的操作数有一个成为函数作用的对象,其余的成为函数的实参。

    必要时需要重载赋值运算符=,以避免两个对象内部的指针指向同一片存储空间。

    运算符可以重载为全局函数,然后声明为类的友元。

    <<和>>是在 iostream 中被重载,才成为所谓的“流插入运算符”和“流提取运算符”的。

    类型的名字可以作为强制类型转换运算符,也可以被重载为类的成员函数。它能使得对象被自动转换为某种类型。

    自增、自减运算符各有两种重载方式,用于区别前置用法和后置用法。

    运算符重载不改变运算符的优先级。重载运算符时,应该尽量保留运算符原本的特性。

    展开全文
  • C++运算符重载

    2021-02-04 15:14:40
    C++运算符重载C++运算符重载C++运算符重载 38.类和对象-C++运算符重载-加号运算符重载 39.类和对象-C++运算符重载-左移运算符重载 40.类和对象-C++运算符重载-递增运算符重载 41.类和对象-C++运算符重载-赋值运算符...
  • C++运算符.doc

    2020-02-19 20:53:21
    C++运算符优先级,是描述在计算机运算计算表达式时执行运算的先后顺序。 先执行具有较高优先级的运算,然后执行较低优先级的运算。 例如,我们常说的先执行相乘和除,再执行加减运算。 C++运算符优先级表,从上...
  • C++ 运算符优先级列表

    2011-10-26 08:10:53
    C++ 运算符优先级列表 C++ 运算符优先级列表 C++ 运算符优先级列表 C++ 运算符优先级列表 C++ 运算符优先级列表
  • C++运算符重载详解

    万次阅读 多人点赞 2016-03-02 20:45:08
    为什么要对运算符进行重载: C++预定义中的运算符的操作对象只局限于基本的内置数据...C++运算符重载的实质: 运算符重载的实质就是函数重载或函数多态。运算符重载是一种形式的C++多态。目的在于让人能够用同名的函
  • C++允许重载的运算符和不允许重载的运算符 C++中绝大部分的运算符允许重载,具体规定见表 ...1) C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载。 例如,有人觉得BASIC中用“**“作为幂运算符很方便

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 34,538
精华内容 13,815
关键字:

c++运算符

c++ 订阅