C++的操作符過載
一、操作符標記和操作符函式
1、雙目操作符:L#R
成員函式形式:L.operator# (R) —— 左調右參
全域性函式形式:::operator#(L,R) —— 左一右二
2、單目操作符:#O/O#
成員函式形式:O.operator#()
全域性函式形式:::operator#(O)
3、三目操作符:不考慮
二、雙目操作符
1、+/-/*//
運算元在計算前後不變;表示式的值是右值。
#include <iostream> using namespace std; class Complex{ public: Complex(int r = 0,int i = 0) : m_r(r), m_i(i){} void print() const{ cout << m_r << "+" << m_i << "i" << endl; } //第一個const:返回右值 //第二個const:支援常量型右運算元 //第三個const:支援常量型左運算元 const Complex operator+(const Complex& r) const{ return Complex(m_r + r.m_r, m_i + r.m_i); } private: int m_r; int m_i; friend const Complex operator-(const Complex&,const Complex&);//友元 }; const Complex operator-(const Complex& l,const Complex& r){ return Complex(l.m_r - r.m_r,l.m_i - r.m_i); } int main(){ Complex c1(1,2); c1.print(); Complex c2(3,4); Complex c3 = c1 +c2;//c3 = c1.operator+(c2); c3.print();//4+6i c3 = c2 - c1;//c3 = ::operator-(c2,c1); c3.print();//2+2i return 0; }
2、複合賦值運算子:+=/-=/*=…
左變右不變
表示式的值是左值,左運算元的引用。
(a += b) = c;
#include <iostream> using namespace std; class Complex{ public: Complex(int r = 0, int i = 0) : m_r(r), m_i(i){} void print(void) const{ cout << m_r << '+' << m_i << 'i' << endl; } Complex& operator+=(const Complex& r){ m_r += r.m_r; m_i += r.m_i; return *this; } friend Complex& operator-= (Complex& l, const Complex& r){//友元函式,是個全域性函式,不是成員函式,寫類裡面和外面都行 l.m_r -= r.m_r; l.m_i -= r.m_i; return l; } private: int m_r; int m_i; }; int main(void){ Complex c1(1,2), c2(3,4); c1 += c2;//c1.operator+= (c2) c1.print();//4+6i Complex c3(5,6); (c1 += c2) = c3; c1.print();//5+6i c1 -= c2;//::operator-= (c1,c2) c1.print();//2+2i (c1 -= c2) = c3; c1.print(); return 0; }
3、<< / >>
int i = 10;
float f = 1.23;
Complex c (…);
cout << c << i << f << endl;
左運算元ostream/istream型別,不能是常量,不能拷貝。
右運算元自定義型別,對於<<可以是常量,對於>>不能是常量。
表示式的值是左運算元的引用。
::operator<< (cout, c).operator<< (i).operator<< (f).operator<< (endl);
#include <iostream>
using namespace std;
class Complex{
public:
Complex(int r = 0, int i = 0) : m_r(r), m_i(i){}
void print(void) const{
cout << m_r << '+' << m_i << 'i' << endl;
}
friend ostream& operator<< (ostream& os,const Complex& r){
return os << r.m_r << '+' << r.m_i << 'i';
}
friend istream& operator>> (istream& is,Complex& r){
return is >> r.m_r >> r.m_i;
}
private:
int m_r;
int m_i;
};
int main(void){
Complex c1(1,2),c2(3,4);
cout << c1 << endl << c2 << endl;
//::operator<< (::operator << (cout,c1).operator<<(endl),c2).operator<< (endl);
cin >> c1 >> c2;
c1.print();
c2.print();
return 0;
}
三、單目操作符
1、-(取負)/!/~
運算元不變。
表示式的值是右值。
#include <iostream>
using namespace std;
class Complex{
public:
Complex(int r = 0, int i = 0) : m_r(r), m_i(i){}
void print(void) const{
cout << m_r << '+' << m_i << 'i' << endl;
}
const Complex operator-(void) const{
return Complex(-m_r,-m_i);
}
friend const Complex operator~(const Complex& o){
return Complex(o.m_i,o.m_r);
}
private:
int m_r;
int m_i;
};
int main(void){
Complex c1(1,2);
Complex c2 = -c1;//c2 = c1.operator-()
c2.print();//-1-2i
Complex c3 = ~c1;//c3 = ::operator~(c1)
c3.print();
return 0;
}
2、前++/前–
運算元變。
表示式的值是運算以後的值。
表示式的值是左值,運算元的引用。
#include <iostream>
using namespace std;
class Complex{
public:
Complex(int r = 0, int i = 0) : m_r(r), m_i(i){}
void print(void) const{
cout << m_r << '+' << m_i << 'i' << endl;
}
Complex& operator++(void){
++m_r;
++m_i;
return *this;
}
friend Complex& operator-- (Complex& o){
--o.m_r;
--o.m_i;
return o;
}
private:
int m_r;
int m_i;
};
int main(void){
Complex c1(1,2);
Complex c2 = ++c1;//c2 = c1.operator++()
c1.print();//2+3i
c2.print();//2+3i
(++c1) = Complex(10,20);
c1.print();//10+20i
(++++++c1).print();
c2 = --c1;//c2=::operator
c1.print();//12+22i
c2.print();//12+22i
return 0;
}
3、後++/後–
運算元變。
表示式的值是運算以前的值。
表示式的值是右值。
#include <iostream>
using namespace std;
class Complex{
public:
Complex(int r = 0, int i = 0) : m_r(r), m_i(i){}
void print(void) const{
cout << m_r << '+' << m_i << 'i' << endl;
}
const Complex operator++(int){
Complex old(*this);
++m_r;
++m_i;
return old;
}
friend const Complex operator-- (Complex& o,int){
Complex old(o);
--o.m_r;
--o.m_i;
return old;
}
private:
int m_r;
int m_i;
};
int main(void){
Complex c1(1,2);
Complex c2 = c1++;//c2=c1.operator++(0)
c1.print();//2+3i
c2.print();//1+2i
//(c1++) = c2;
//c1++++++;
c2 = c1--;//c2::operator--(c1,0)
c1.print();//1+2i
c2.print();//2+3i
return 0;
}
四、其他操作符
1、下標操作符:[]
雙目操作符,左運算元是一個具有容器特性的物件,右運算元是容器中特定資料元素的索引(基零的下標)。
下標表達式的值可以是左值,也可以是右值,由容器物件的常屬性決定。常容器下標表達式的值是右值,非常容器下標表達式的值是左值。
#include <iostream>
using namespace std;
class Array{
public:
Array(size_t size = 1) : m_data(new int[size]){}
~Array(void){
if (m_data){
delete m_data;
m_data = NULL;
}
}
int& operator[] (size_t i){
return m_data[i];
}
const int& operator[] (size_t i) const{
return const_cast<Array&> (*this)[i];
}
private:
int* m_data;
};
int main(void){
Array arr(10);
for (size_t i = 0;i < 10;++i)
arr[i] = i;//arr.operator[](i) = i;
arr[0]++;
const Array& cr = arr;
for (size_t i = 0;i < 10;++i)
cout << cr[i] << ' ';
cout << endl;
//cr[0]++;
return 0;
}
2、函式操作符:()
如果為一個類定義了形如:
返回型別 operator() (形參表){…}
的操作符函式,那麼這個類所例項化的物件就可以被當做函式使用。
3、解引用(*)和間接訪問(->)操作符
以指標的方式使用類型別的物件。
#include <iostream>
#include <memory>
using namespace std;
class A{
public:
A(void){
cout << "構造" << endl;
}
~A(void){
cout << "析構" << endl;
}
void hello(void){
cout << "Hello,World!" << endl;
}
};
class PA{
public:
PA(A* p = NULL) : m_p(p){}
~PA(void){
if (m_p){
delete m_p;
m_p = NULL;
}
}
A& operator*(void) const{
return *m_p;
}
A* operator->(void) const{
return &(**this);
}
PA(PA& that) : m_p(that.release()){}
PA& operator= (PA& that){
if (&that != this)
reset(that.release());
return *this;
}
private:
A* m_p;
A* release(void){
A* p = m_p;
m_p = NULL;
return p;
}
void reset(A* p){
if (p != m_p){
delete m_p;
m_p = p;
}
}
};
void bar(auto_ptr<A> pa){
cout << "bar:";
pa->hello();
}
void foo(void){
//A* pa = new A;
//delete pa;
//A a;
PA pa(new A);
pa -> hello();//pa.operator->()->hello();
(*pa).hello();//pa.operator*().hello();
PA pb = pa;
pb->hello();//pa就不能用啦
auto_ptr<A> pa1(new A);//智慧指標模板
pa1->hello();
auto_ptr<A> pa2 = pa1;
(*pa2).hello();
bar(pa2);
cout << pa2.get() << endl;//get()函式取指標的值
}
//smart_ptr,2011標準中的智慧指標,可實現
int main(void){
foo();
return 0;
}
4、自定義型別轉換和型別轉換操作符
(1)通過單參構造實現自定義型別轉換
如果A類中有一個可以接受B類物件作為唯一引數的建構函式,那麼B型別的物件就可以根據該建構函式被轉換為A型別。
通過explicit關鍵字,可以強制使用該建構函式所完成的型別轉換必須顯示進行。
(2)通過型別轉換操作符函式實現自定義型別轉換,如果A類中有一個形如
operator B (void) const{ … }
的操作符函式,那麼A型別的物件就可以根據該函式被轉換為B型別。
如果目標型別是類型別,源型別是基本型別,那麼就只能通過在目標型別中定義以源型別為單參的建構函式實現型別轉換。
如果目標型別是基本型別,源型別是類型別,那麼就只能通過在源型別中定義以目標型別為函式名的型別轉換操作符函式實現型別轉換。
如果目標型別和源型別都是類型別,那麼以上兩種方法任取其一,但是不能同時使用。
如果目標型別和源型別都是基本型別,那麼無法實現自定義型別轉換。
#include <iostream>
using namespace std;
class Square{
public:
double operator() (double x){
return x * x;
}
};
class Integer{
public:
explicit Integer(int i = 0) : m_i(i){}//explicit關鍵字規定使用顯示單參構造型別轉換
void print(void) const{
cout << m_i << endl;
}
Integer& operator()(int i){
m_i += i;
return *this;
}
operator int(void) const{//型別轉換操作符
return m_i;
}
private:
int m_i;
};
void foo(const Integer& i){
i.print();
}
Integer bar(void){
return Integer(300);
}
int main(void){
Square square;
cout << square(3) << endl;
//cout << square.operator()(3) << endl;
Integer i(10);
i(1)(2)(3)(17);
i.print();//33
i = (Integer)100;
i.print();
foo(static_cast<Integer> (200));
bar().print();
int n = i;
cout << n << endl;
return 0;
}
5、new/delete操作符
#include <iostream>
#include <cstdlib>
using namespace std;
class A{
public:
~A(void){}
static void* operator new (size_t size){
void* p = malloc(size);
cout << "自定義new:" << p << ' ' << size << endl;
return p;
}
static void operator delete(void* p){
cout << "自定義delete:" << p << endl;
free(p);
}
static void* operator new[] (size_t size){
void* p = malloc(size);
cout << "自定義的new[]:" << p << ' ' << size << endl;
return p;
}
static void operator delete[] (void* p){
cout << "自定義的delete[]:" << p << endl;
free(p);
}
private:
int m_i;
double m_d;
char m_c;
};
int main(void){
cout << sizeof(A) << endl;
A* pa = new A;
cout << pa << endl;
delete pa;
pa = new A[2];
cout << pa << endl;
delete[] pa;
return 0;
}
五、關於操作符過載的限制
1、至少有一個運算元是類型別的。
int a = 10, b = 20;
int c = a + b;//200
int operator+ (int a, int b){
return a * b;
}//ERROR!
2、不是所有的操作符都能過載。
:: —— 作用域限定
. —— 直接成員訪問
.* —— 直接成員指標解引用
?: —— 三目運算子
sizeof —— 獲取位元組數
typeid —— 獲取型別資訊
3、不是所有的操作符都可以用全域性函式的方式實現。
= —— 拷貝賦值
[] —— 下標操作符
() —— 函式操作符
-> —— 間接成員訪問