【C++】重载
参考:黄邦勇帅
1.操作符重载函数作为类的成员和友元或者独立于类的区别:
当操作符重载函数作为类的成员函数时,操作符重载函数的参数会比作为友元或者独立于类的操作符重载函数少一个参数,因为操作符重载类成员函数把调用该函数的第一个类的对象作为函数的第一个参数,也就是隐含的 this 指针指向调用该函数的第一个对象,所以会少一个参数。
2.调用操作符重载函数的方式:
调用类中的操作符重载函数的方法:比如在类 hyong 中重定义的+操作符 hyong operator +(hyong m){},有类 hyong 的对象 m和 n 则调用操作符重载函数的方法有 m+n 和 m.operator +(n),前一条语句会自动转换为后面这条语句
调用友元或独立的操作符重载函数的方法:当调用类的友元操作符重载函数或独立的操作符函数时语句 m+n会转换为显示的调用方式,比如有友元或独立操作符重载函数 hyong operator +(hyong a, hyong b){}则当出现m+n 时会转换成语句 operator +(m, n)表达式的第一个对象传给第一个参数,第二个对象传给第二个参数。
3.什么情况下需要把操作符函数作为类的友元或者类的成员
有一种情况必须要求操作符函数作为类的友元函数或者是独立的函数,就是一个内置类型和对象相加的情况。比如有语句 m+1 和 1+m第一条可以在类中定义操作符函数的形式为 hyong operator +(int i){},语句 m+1 可以调用这个函数是正确的,但对于 1+m 就不能调用这个函数了,因为类中的操作符重载函数是最左边的对象是调用该函数的对象,但 1+m最左边的是一个内置整型类型 1,所以不会调用这条语句,对于这种语句就只能把操作符重载函数定义为独立的函数或类的友元函数即形如hyong operator +(int i , hyong a){}这样 1+m就会转换成 operator +(1, m)这样就是正确的。
如果这个操作符重载函数需要访问类中的私有成员时,就应把该函数定义为类的友元函数,如果不需要访问类中的私有成员,则可以定义为友元也可以定义为独立函数。
4.必须把操作符函数作为类成员函数的运算符有:(),[],->和任何赋值运算符,重载这些运算符时必须把操作符函数声明为类的成员函数。
5.重载操作符的限制:
并不是所有的操作符都能被重载。除了. ,.* ,:: ,? : ,sizeof,typeid 这几个运算符不能被重载,其他运算符都能被重载
重载不能改变该运算符用于内置类型时的函义
运算符函数的参数至少有一个必须是类的对象或者类的对象的引用。这种规定可以防止程序员运用运算符改变内置类型的函义。
重载不能改变运算符的优先级。
重载不能改变运算符的结合律。
重载不能改变运算符操作数的个数
6.反回类型问题:可以在语句中反回一个临时对象,也可以反回一个对象的引用,或者反回 this 指针,不过反回临时对象会浪费内存开销,所以最好反回类对象的一个引用。
7.参数传递问题:操作符函数可以按值传递也可以按引用传递,这根据操作符而定,比如对于+运算符既可以把对象按值传递给操作符函数也可以按引用传递给操作符函数,而且+操作符不会改变 原操作数的值 ,所以应把传递类型声明为 const,比如 hyong operator +(const hyong &a, const hyong &b){}。但对于要改变其自身值的操作符比如++运算符,就必须传递引用,且不能把该引用声明为 const 类型,因为如果操作数按值传递的话,传递给操作数函数的 将是一个对象的副本,两个副本是独立的,不能改变到原对象的值,所以应按引用传递对象
重载++运算符
默认++是前缀的,后缀形式要定义一个新的形式。
后缀++由于会产生临时变量,所以效率低于前缀++
#include <iostream> using namespace std; class A { public: int b; A() { b = 0; } A(int i) { b = i; } const A & operator++() //前缀++ { ++b; return *this; } }; const A & operator++(A &j, int i) //后缀++ 会产生一个临时变量,效率低 后面的int i只是为了区分前缀++ { A t(j); ++j.b; return t; } int main() { A m, n, k; m = ++k; //调用了前缀++ cout << m.b << k.b << endl; n = k.operator++(); //显式调用前缀++ cout << n.b << k.b << endl; n = k++; //调用了后缀++ cout << n.b << k.b << endl; n = operator++(k, 1); //显式调用后缀++ cout << n.b << k.b << endl; return 0; }
重载+运算符
注意内置类型+对象的情况
#include <iostream> using namespace std; class A { public: int b; A() { b = 0; } ~A() { cout << "A 析构函数" << endl; } explicit A(int i) //不允许隐式转换 { b = i; } const A & operator+(const A &j) //1 { A t; t.b = b + j.b; return t; } friend const A &operator+(const A &j, const int i); }; const A & operator+(const A &j, const int i) //2 实现 对象+内置类型 { A t; t.b = j.b + i; return t; } const A & operator+(const int i, const A &j) //3 实现 内置类型+对象 因为没有使用类的私有变量,可以不定义为友元 { A t; t.b = j.b + i; return t; } int main() { A m(1), n(2), k; k = m + 3; //调用2 cout << m.b << k.b << endl; k = operator+(m, 3); //调用2 cout << m.b << k.b << endl; k = m + n; //调用1 cout << m.b << n.b << k.b << endl; k = m.operator+(n); //调用1 cout << m.b << n.b << k.b << endl; k = 4 + m; //调用3 cout << m.b << k.b << endl; return 0; }
重载=运算符
1.注意重载赋值运算符和[],(),->运算符必须定义为类的成员函数。
2.重载赋值运算符时应有 处理语句 m=m 的情况。其中 m 是某一个类的对象。如果不处理这样的语句有时会出现问题,具体什么问题有待调查。可以用 this 指针来做处理,比如有语句 const A & operator(A &j)则可以用 if(this==&j) return*this;这样的语句来处理。
3.重载赋值运算符时应反回一个对象。因为赋值运算符的左边是一个对象,所以重载赋值运算符应反回一个类的对象,为了避免不必要的开销,最好是反回一个类的对象的引用。
4.如果程序不 提供显示的赋值运算符则系统会提供一个默认的赋值运算符 。
#include <iostream> using namespace std; class A { public: int b; A() { b = 0; } A(int i) //不允许隐式转换 { b = i; } const A & operator=(const A & j) { if(this == &j) //需要处理自己等于自己的情况 return *this; b = j.b; return *this; } }; int main() { A m(2); A n; n = m; n = n; return 0; }