程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++哪些運算符重載可以重載?

C++哪些運算符重載可以重載?

編輯:C++入門知識

C++哪些運算符重載可以重載?


運算符重載是C++極為重要的語言特性之一,本文將用代碼實例回答——C++哪些運算符可以重載?如何重載?實現運算符重載時需要注意哪些?


哪些運算符可以重載,哪些不可重載?

C++98,C++0x,C++11對“哪些運算符重載可以重載”有一致的規定,具體如下:

\

其中,很少使用的是“,”(逗號運算符)。<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+PGJyPgo8L3A+CjxwPrHq17zNrNH5uea2qMHLPHN0cm9uZz6yu7/J1tjU2LXE1MvL47f7PC9zdHJvbmc+o7o8L3A+CjxwPjxpbWcgc3JjPQ=="http://www.2cto.com/uploadfile/Collfiles/20140930/20140930092431312.png" alt="\">

其中,“::”是作用域運算符,

“?:”是條件運算符。


兩個較少使用的運算符是 .* 和 ->* 分別是:

.* 對象調用成員函數指針;

->* 對象指針調用成員函數指針;


標准還指出,有的運算符可以同時支持“一元運算”和“二元運算”:

\




下文通過實現幾個模擬內置類型的類來展示具體的運算符重載方法應當如何實現。


一些注意事項

實現運算符重載時應注意:

運算符重載不改變運算符的優先級與結合性,如/的優先級比-高;運算符重載不改變部分運算符對操作數個數的限制,如+只能有兩個操作數;


模擬整型Integer

回想一下C++裡整型支持支持那些運算?具體有:

算術運算:

正,如 +a;負,如 -a;

加,如 a + b;減,如 a - b;乘,如 a * b;除,如 a / b;取余(模),如 a % b;


自增自減(整型特有):

自增,如 a++,++a;自減,如 a--,--a;


比較運算:

大於,如 a > b;小於,如 a < b;

等於,如 a == b;不等於,如 a != b;

大於等於,如 a >= b;小於等於,如 a <= b;


位運算:

按位取反,如 ~a;

左移,如 a << 2;右移,如 a >> 3;

按位與,如 a & b;按位或,如 a " b;按位異或,如 a ^ b;


賦值運算:

賦值,a = 5;


復合賦值:

+=,-=,*=,/=,%=,(算數運算與賦值運算復合)

&=,|=,^=,<<=,>>=,(位運算與賦值運算復合)


下面是這個Integer的代碼:

class Integer
{
public:
	Integer(int ival) : value(ival) {}
	Integer(const Integer& iobj) : value(iobj.value) {}
	
	// operator int() { return value; } // conversion to built-in int

	Integer operator+() const { return Integer(value); }
	Integer operator-() const { return Integer(-value); }
	
	Integer operator+(const Integer& rhs) const { return Integer(value + rhs.value); }
	Integer operator-(const Integer& rhs) const { return Integer(value - rhs.value); }
	Integer operator*(const Integer& rhs) const { return Integer(value * rhs.value); }
	Integer operator/(const Integer& rhs) const { return Integer(value / rhs.value); }
	Integer operator%(const Integer& rhs) const { return Integer(value % rhs.value); }
	
	// prefix
	Integer operator++() { return Integer(++value); }
	Integer operator--() { return Integer(--value); }
	
	// suffix
	Integer operator++(int) { int old = value; value++; return Integer(old); }
	Integer operator--(int) { int old = value; value--; return Integer(old); }

	// compare:
	bool operator<(const Integer& rhs) const { return value < rhs.value; }
	bool operator>(const Integer& rhs) const { return value > rhs.value; }
	bool operator==(const Integer& rhs) const { return value == rhs.value; }
	bool operator!=(const Integer& rhs) const { return value != rhs.value; }
	bool operator<=(const Integer& rhs) const { return value <= rhs.value; }
	bool operator>=(const Integer& rhs) const { return value >= rhs.value; }
	
	// bit operations:
	Integer operator~() const { return Integer(~value); }
	Integer operator<<(unsigned n) const { return Integer(value << n); }
	Integer operator>>(unsigned n) const { return Integer(value >> n); }
	Integer operator&(const Integer& rhs) const { return Integer(value & rhs.value); }
	Integer operator|(const Integer& rhs) const { return Integer(value | rhs.value); }
	Integer operator^(const Integer& rhs) const { return Integer(value ^ rhs.value); }

	// assignment:
	Integer operator=(const Integer& rhs) { return value = rhs.value; }
	// compound assignment:
	Integer operator+=(const Integer& rhs) { return value += rhs.value; }
	Integer operator-=(const Integer& rhs) { return value -= rhs.value; }
	Integer operator*=(const Integer& rhs) { return value *= rhs.value; }
	Integer operator/=(const Integer& rhs) { return value /= rhs.value; }
	Integer operator%=(const Integer& rhs) { return value %= rhs.value; }
	Integer operator&=(const Integer& rhs) { return value &= rhs.value; }
	Integer operator|=(const Integer& rhs) { return value |= rhs.value; }
	Integer operator^=(const Integer& rhs) { return value ^= rhs.value; }
	Integer operator<<=(const Integer& rhs) { return value <<= rhs.value; }
	Integer operator>>=(const Integer& rhs) { return value >>= rhs.value; }
// private:
	int value;
};

實現運算符重載函數時,需要注意的是末尾是否要加const?
這取決與操作是否會改變當前對象的成員值,如果不改變則不加,改變則加。

Integer類只是為了展示如何使用運算符重載,並沒有多少實用價值。

一下是Integer類的測試:

void testInteger()
{
	Integer i = 123;

#define SEPRATER ":\t"
#define TRACE_INTEGER(iobj) printf(#iobj SEPRATER "%d\n", (iobj).value) 
#define TRACE_BOOL(exp) printf(#exp SEPRATER "%s\n", (exp) ? "true" : "false")
#define TRACE_HEX(iobj) printf(#iobj SEPRATER "%p\n", (iobj).value)

	TRACE_INTEGER(i);
	TRACE_INTEGER(+i);
	TRACE_INTEGER(-i);
	
	Integer j = 5;
	TRACE_INTEGER(i+j);
	TRACE_INTEGER(i-j);
	TRACE_INTEGER(i*j);
	TRACE_INTEGER(i/j);
	TRACE_INTEGER(i%j);
	
	TRACE_INTEGER(++i); TRACE_INTEGER(i);
	TRACE_INTEGER(--i); TRACE_INTEGER(i);
	TRACE_INTEGER(i++); TRACE_INTEGER(i);
	TRACE_INTEGER(i--); TRACE_INTEGER(i);
	
	TRACE_BOOL(i>j);
	TRACE_BOOL(i=j);
	TRACE_BOOL(i<=j);
	
	TRACE_HEX(i);
	TRACE_HEX(~i);
	TRACE_HEX(i<<4);
	TRACE_HEX(i>>4);
	TRACE_HEX(i<<24);
	
	TRACE_HEX(i & ~0xF); // i & ~0xF <<== same as ==>> i & Integer(~0xF), because C++ implicit conversion.
	TRACE_HEX(i | 0xF0);
	TRACE_HEX(i ^ 0xF0);
	
	TRACE_INTEGER(i);
	TRACE_INTEGER(j);
	TRACE_INTEGER(i=j);
	
	TRACE_INTEGER(i+=j);
	TRACE_INTEGER(i-=j);
	TRACE_INTEGER(i*=j);
	TRACE_INTEGER(i/=j);
	
	j = 3;
	TRACE_INTEGER(i%=j);
}
該測試的輸出如下:

i:	123
+i:	123
-i:	-123
i+j:	128
i-j:	118
i*j:	615
i/j:	24
i % :	3
++i:	124
i:	124
--i:	123
i:	123
i++:	123
i:	124
i--:	124
i:	123
i>j:	true
i=j:	true
i<=j:	false
i:	0000007B
~i:	FFFFFF84
i<<4:	000007B0
i>>4:	00000007
i<<24:	7B000000
i & ~0xF:	00000070
i | 0xF0:	000000FB
i ^ 0xF0:	0000008B
i:	123
j:	5
i=j:	5
i+=j:	10
i-=j:	5
i*=j:	25
i/=j:	5
i%=j:	2


模擬指針Pointer

再回想一下原生的指針支持那些運算?

具體有:

*,解引用,從T*得到T

[],下標運算

++,自增

--,自減

+,加法

-,減法

有一點值得注意的是:

每種類型都對應一種指針,如int對應int*;解引用和下標運算的返回結果可以做“左值”(賦值運算符左邊的值,即可以賦值);指針的加減,-是以指針當前類型的大小為單位的,即:若有指針p,則 (size_t)(p + 1) == (size_t)p + sizeof(*p)

要實現1,Pointer類型必須實現為類模板;

Pointer實現為類模板,它的數據成員(data member)就可以是原生指針,可以很自然的支持原生指針的加減運算。

據此實現的Pointer類如下:

template 
class Pointer 
{
public:
	Pointer() : ptr_(0) {}
	Pointer(T* ptr) : ptr_(ptr) {}
	
	// operator T*() { return ptr_; } // implicit conversion.
	T* get() const { return ptr_; }	
	
	T& operator*() const { return *ptr_; }
	T* operator->() const { return ptr_; printf("operator->()\n"); }
	T& operator[](int offset) const { return ptr_[offset]; }
	
	Pointer operator++() { return Pointer(++ptr_); } // prefix
	Pointer operator--() { return Pointer(--ptr_); } // prefix
	
	Pointer operator++(int) { return Pointer(ptr_++); } // suffix
	Pointer operator--(int) { return Pointer(ptr_--); } // suffix
	
	Pointer operator+=(int off) { return Pointer(ptr_ += off); }
	Pointer operator-=(int off) { return Pointer(ptr_ -= off); }
	
	Pointer operator+(int off) const { return Pointer(ptr_ + off); }
	Pointer operator-(int off) const { return Pointer(ptr_ - off); }
// private:
	T* ptr_;
};

Pointer類僅模擬指針的一般運算,並沒有考慮“通過指針進行資源管理”這一主題,所有也沒有保證delete ptr的實際行為。


以下是該類的測試程序:

template
ostream& operator<<(ostream& out, const Pointer& ptr)
{
	out << ptr.ptr_;
	return out;	
}

//#define TRACE(fmt, exp) printf(#exp ":\t" fmt, (exp))
#define TRACE(exp) cout << #exp << ":\t" << (exp) << endl
#define ARRAY_SIZE(a) sizeof(a)/sizeof(a[0])

int ia[] = { 123, 456, 789, 111, 222, 333 };

void testPointer()
{
	for(int i=0; i ptr = &ia[0];
	
	TRACE(ptr);
	TRACE(ia);
	TRACE(*ptr);
	TRACE(ptr[1]);
	
	TRACE(++ptr); TRACE(*ptr);
	TRACE(--ptr); TRACE(*ptr);
	
	TRACE(ptr++); TRACE(*ptr);
	TRACE(ptr--); TRACE(*ptr);
	
	TRACE(ptr+=2); TRACE(*ptr);
	TRACE(ptr-=2); TRACE(*ptr);
	
	ptr = &ia[3];
	TRACE(ptr+2); TRACE(*(ptr+2));
	TRACE(ptr-2); TRACE(*(ptr-2));
	
	ptr[0] = 555;
	TRACE(ptr[0]);
	
	*ptr = 666;
	TRACE(*ptr);
}

測試程序的輸出如下:

0x603090: 123
0x603094: 456
0x603098: 789
0x60309c: 111
0x6030a0: 222
0x6030a4: 333
ptr:	0x603090
ia:	0x603090
*ptr:	123
ptr[1]:	456
++ptr:	0x603094
*ptr:	456
--ptr:	0x603090
*ptr:	123
ptr++:	0x603090
*ptr:	456
ptr--:	0x603094
*ptr:	123
ptr+=2:	0x603098
*ptr:	789
ptr-=2:	0x603090
*ptr:	123
ptr+2:	0x6030a4
*(ptr+2):	333
ptr-2:	0x603094
*(ptr-2):	456
ptr[0]:	555
*ptr:	666


上面的測試testPointer並沒有用到operator->,下面單獨測試operator->:

struct Foo 
{
	int id;
	static int count;
	
	Foo() : id(++count) { printf("Foo::Foo(%p)\n", this); }
	~Foo() { printf("Foo::~Foo(%p)\n", this); }
	
	void show() { printf("Foo.show(%p): %d\n", this, id); }
};

int Foo::count = 0;

void testAccess()
{
	printf("\n%s():\n", __FUNCTION__);
	Pointer fptr = new Foo();
	
	// test operator->
	fptr->show();     // access member function.
	TRACE(fptr->id);  // access data member.
	
	delete fptr.get();
}
該測試程序的輸出為:

testAccess():
Foo::Foo(0x133a010)
Foo.show(0x133a010): 1
fptr->id:	1
Foo::~Foo(0x133a010)


幾種特殊運算符的重載

上面的兩個類基本已經覆蓋了大部分的運算符重載,下面展示幾個“罕見”的運算符重載:

#define TRACE_CALL puts(__FUNCTION__)
class Operand
{
public:
	void operator,(const Operand& rhs) const { TRACE_CALL; }
	void operator,(int a) const { TRACE_CALL; }
	// void operator,(int a, int b) const { TRACE_CALL; } // ERROR: operator, 有且僅有一個參數
	void operator->*(int a) const { printf("%s(%d)\n", __FUNCTION__, a); }
};
測試程序:

void testOperand()
{
	Operand op1, op2;

	op1, op2;  // operator,
	op1->*123; // operator->*
}

程序輸出:

operator,
operator->*(123)

operator new與operator delete

關於operator new和operator delete的重載,下次另外寫博客闡釋。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved