运算符的重载

运算符的重载
1.运算符重载的两种方法:
类成员函数运算符重载
  •  return_type class_name::operator op(operand2) {}
  •  重载二元运算符时,成员运算符函数只需显式传递一个参数(即二元运算符的右操作数), 而左操作数则是该类对象本身,通过 this 指针隐式传递。
  • 重载一元运算符时,成员运算符函数没有参数,操作数是该类对象本身,通过 this 指针隐式 传递
 友元函数运算符重载
  • return_type operator op(operand1, operand2) {}
友元函数运算符 重载原则:
  • 函数的形参代表依自左至右次序排列的各操作数。
  • 参数个数=原操作数个数(后置++、后置--除外)
  • 至少应该有一个自定义类型的参数。
  • 后置单目运算符 ++和–-的重载函数,形参列表中要增加一个int,但不必写形参名。
  • 如果在运算符的重载函数中需要操作某类对象的私有成员,可以将此函数声明为该类的友元。
2.可以重载的运算符
3、不可以重载的运算符
注意: operator重载运算符时 不能覆盖原有的运算 , 即操作数中必须 至少有一个是 自建类型 , 这虽然限制了一点操作性, 但保护了程序的正常执行.
4.重载自增运算符时需注意:
• 若为前缀自增运算符,直接重载(以++举例):
• return_type class_name::operator ++() {}
• 若为后缀自增运算符,该函数有一个 int 类型的虚拟形参,这个形参在函数的主体中是不会被使
用的,这只是一个约定,它告诉编译器递增运算符正在后缀模式下被重载:
• return_type class_name::operator ++(int) {}
5.赋值运算符
class Integer {
    int x;
public :
    Integer( int x =0):x( x )   {
    }
    Integer operator + ( const Integer & Int ) {  
        return Integer (x+ Int .x);
    }
    Integer operator - ( const Integer & Int ) {
        return Integer (x- Int .x);
    }
    Integer operator - () {
        return Integer (-x);
    }
    void print() {
        cout << x << endl;
    }
};
int main()  {
    Integer a=3,b=4;
    a.print();
    b.print();
    Integer c=a + b;
    c.print();
    Integer d=a - b;
    d.print();
    Integer e= - a;
    e.print();
   
    //d = a + 4;正确, 当形参为const Integer/ const Integer&/ Integer 时,4作为参数传进来,开辟空间,调用了构造函数,创建了对象,发生了隐式转换。
    //e = 4 + a;error,第一次操作数不是对象,不能发生隐式转换。
   
    return 0;
}
5.重载原则
(1) 除了类属关系运算符"."、成员指针运算符".*"、作用域运算符"::"、sizeof运算符和三目运算符"?:"以外,C++中的所有运算符都可以重载。
(2) 重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中, 不能创建新的运算符
(3) 运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循 函数重载 的选择原则。
(4) 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构。
(5) 运算符重载不能改变该运算符用于内部类型对象的含义。它只能和用户自定义类型的对象一起使用,或者用于用户自定义类型的对象和内部类型的对象混合使用时。
(6) 运算时,有数和对象的混合运算时,必须使用友元
(7) =,(),[], ->不能以友元方式重载
(8) 二元运算符中,第一个操作数为非对象时,必须使用友元函数。如输入输出运算符<<和>>
为什么输出运算符重载不能是一个成员函数?而非得声明为友元?
  • 如果要重载<<运算符,一般写法是这样的
  • friend ostream& operator<<(ostream& os, const classType& obj);
  • friend istream& operator<<(istream& os, const classType& obj);
  • 则第一个参数是该运算符的第一个操作数,然而,却不是该类对象,
  • 所以当该类运算符重载时写在类的内部时,又为了访问类内除public外的其它变量或函数,
  • 则应当声明为友元函数:
  • friend ostream& operator<<(ostream& os, const classType& obj);

6.重载输入输出操作符<< >>实例

class Point  
{  
private:  
    int x;
public:  
    Point(int x1)
    {      x=x1;}
    friend ostream& operator<<(ostream& cout,const Point& p);//使用友元函数重载<<输出运算符
    friend istream& operator>>(istream& cin,Point& p);//使用友元函数重载>>输出运算符
};  
ostream& operator<<(ostream& cout,const Point& p)
{
    cout<<p.x<<endl;
    return cout;
}
istream& operator>>(istream& cin,Point& p)
{
    cin>>p.x;
    return cin;
}

7、类型转换运算符重载

转换构造函数(包括拷贝构造函数)
  • 普通类型到类类型的转换

转换构造函数(特殊的构造函数)
满足下列条件的构造函数称为转换构造函数:
1. 一个参数 的构造函数(或者除了 第一个 参数外其余参数都有默认值的多参构造函数,当然啦,第一个参数可以有默认值,也可以没有默认值)
2. 参数是基本类型
3. 参数是 其它 类类型
class Integer {
    int x;
public :
    Integer( int x = 0) :x( x ) {}
    friend Integer operator+ ( const Integer & lhs , const Integer & rhs ) {
        return lhs .x + rhs .x; //① int->Integer
    }
    friend ostream & operator<< ( ostream & o , const Integer & hs ) {
        o << hs .x;
        return o ;
    }
};
int main() {
    string s;
    s = "Hello" ; //②const char* ->string
    cout << s << endl;
    Integer i1(3), i2;
    i2 = 1.1 + i1;
    //③double ->int int->Integer所以1.1+i1相当于operator+( Integer(1), i1),调用了转换构造函数
    cout << i2 << endl;
}
隐式转换——重载协议(const)
如果重载的函数参数一样,可以通过转换到某 个重载函数,编译会选择哪个版本?
(1)类型直接匹配的优先选择;
(2)const 类型实参匹配 const 版本
(3)非 const 实参优先匹配非 const 版本, 没有则隐式转换为 const 版本匹配。
class Integer {
        int x;
public :
       Integer( int x = 0) :x( x ) {}
        friend Integer operator+ ( const Integer & lhs , Integer rhs ) {
               return lhs .x + rhs .x;
       }
        friend Integer operator+ ( const Integer & lhs , Integer * rhs ) {
               if ( rhs ) return lhs .x + rhs ->x + 2000;
               else return lhs .x + 1000;
       }
        friend ostream & operator<< ( ostream & o , const Integer & hs ) {
               o << hs .x; return o ;
       }
};
class Func { /* ... */ };
void func( Func * p ) { cout << "pointer" << endl; };
void func( int a ) { cout << "int" << endl; };
int main() {
       func( nullptr ); // 输出:pointer
       func( NULL ); // 输出:int
        Integer i1(1), i2;
       i2 = i1 + &i1; // 尝试 1,nullptr,NULL,&i1 取代 0
        //0输出1001
        //1输出2
        //nullptr输出1001
        //NULL输出1001
        //&i1输出2002
       cout << i2 << endl;
}
explicit
C++提供了关键字explicit,可以 阻止不应该允许的经过转换构造函数进行的隐式转换的发生,声明为explicit的构造函数不能在隐式转换中使用。
C++中, 一个参数的构造函数(或者除了 第一个参数外其余参数都有默认值的多参构造函数), 承担了两个角色。 1 是个构造;2 是 个默认且隐含的类型转换操作符。
所以, 有时候在我们写下如 AAA = XXX, 这样的代码, 且恰好XXX的类型正好是AAA单参数构造器的参数类型, 这时候编译器就自动调用这个构造器, 创建一个AAA的对象。
这样看起来好象很酷, 很方便。 但在某些情况下, 却违背了程序员的本意。 这时候就要在这个构造器前面加上explicit修饰, 指定这个构造器只能被明确的调用/使用, 不能作为类型转换操作符被隐含的使用。
类型转换函数
1. C++类中可以定义类型转换函数
  • 类型转换函数用于将类对象转换为其它类型。
  • 函数原型为: operator Type(){} ;Type表示 类类型转换成的其他类型,即返回的类型
2. 类型转换函数与转换构造函数具有同等的地位
3. 使得编译器有能力将对象转化为其它类型
4. 编译器能够隐式的使用类型转换函数
  • 类类型到普通类型的转换

class Test
{
    int mValue;
public :
    Test( int i = 0)
    {
        mValue = i ;
    }
    int value()
    {
        return mValue;
    }
    operator double ()
    {
        return mValue;
    }
};
int main()
{
    Test t(100);
    double i;
    i=t;//等效于double i=t;
//表示将t中的mValue转换成double类型返回,赋值给i
    cout << "t.value() = " << t.value() << endl;
    cout << "i = " << i << endl;
    return 0;
}
  • 类类型之间的转换

#include <iostream>
#include <string>
using namespace std;
class Value
{
public:
    Value()
    {
    }
    Value(Test& t)
    {
        cout << "explicit Value(Test& t)" << endl;
    }
};
class Test
{
    int mValue;
public:
    Test(int i = 0)
    {
        mValue = i;
    }
    int value()
    {
        return mValue;
    }
    operator Value()
    {
        Value ret;
        cout << "operator Value()" << endl;
        return ret;
    }
};
int main()
{   
    Test t(100);
    Value v = t;
    return 0;
}
从输出结果我们可以发现:转换构造函数和类型转换函数发生冲突了,编译器 不知道应该调用哪个函数 。因此发生了错误。
当然我们可以使用explicit关键字抑制隐式的转换构造函数,让程序只调用类型转换函数。但是, 我们无法抑制隐式的类型转换函数
补充: static_cast是一个强制类型转换操作符。强制类型转换,也称为显式转换

8、类成员访问运算符 -> 重载

类成员访问运算符( -> )可以被重载,但它较为麻烦。它被定义用于为一个类赋予"指针"行为。运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象
运算符 -> 通常与指针引用运算符 * 结合使用,用于实现"智能指针"的功能。这些指针是行为与正常指针相似的对象,唯一不同的是,当您通过指针访问对象时,它们会执行其他的任务。比如,当指针销毁时,或者当指针指向另一个对象时,会自动删除对象。
间接引用运算符 -> 可被定义为一个一元后缀运算符。也就是说,给出一个类:
class Ptr{
//...
X * operator->();};
 Ptr 的对象可用于访问类 X 的成员,使用方式与指针的用法十分相似。例如:
void f ( Ptr p ) {
p -> m = 10 ; // (p.operator->())->m = 10
}
语句 p->m 被解释为 (p.operator->())->m。同样地,下面的实例演示了如何重载类成员访问运算符 ->。
class A {
public :
        int a;
       A( int a =37):a( a ) { }
        void print() { cout << a << endl; }
       
};
class B {
public :
        int b;
       B() { b = 11; };
        operator A () {
               return A (b);
       }
        A * operator-> () {
               A * a = new A ;
              *a = * this ;
               return a;
       }
};
int main() {
        A a;
        B b;
       b -> print();
        return 0;
}
9、赋值运算符重载
class T
{
private :
        int * mArray;
        int size;
public :
       T& operator =( const T& other)
       {
               if ( this != &other)//期待检查自复制
               {
                        if (other.size != size)//
                       {
                              delete []mArray;
                             size = 0;
                              mArray = nullptr ;
                              mArray = new int [other.size];
                              size = other.size;
                       }
           std::copy(other.mArray, other.mArray + other.size, mArray);      
                }
          return * this ;
         }
}; 
copy(被复制对象首地址/begin(),被复制对象尾地址/end(),复制对象的首地址/begin())
    //declaring & initializing an int array
    int arr[] = { 10, 20, 30, 40, 50 };
    
    //向量声明
    vector<int> v1(5);
    
    //复制数组元素到向量
    copy(arr, arr + 5, v1.begin());
    Output:
    //如果我们打印值
    arr: 10 20 30 40 50
    v1: 10 20 30 40 50
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值