運算符重載
函式
自定義類的
賦值運算符重載函式的作用與內置賦值運算符的作用類似,但是要注意的是,它與
拷貝構造函式與
析構函式一樣,要注意深拷貝淺拷貝的問題,在沒有深拷貝淺拷貝的情況下,如果沒有指定默認的賦值運算符重載函式,那么系統將會自動提供一個賦值運算符重載函式。
示例
下面是
Vector的定義—— 包含成員欄位、
構造函式和一個ToString()重寫方法,以便查看Vector的內容,最後是運算符重載:
namespace Wrox.ProCSharp.OOCSharp
{
struct Vector
{
public double x,y,z;
public Vector(double x,double y,double z)
{
this.x = x;
this.y = y;
this.z = z;
}
public Vector(Vector rhs)
{
x = rhs.x;
y = rhs.y;
z = rhs.z;
}
public override string ToString()
{
return "( " + x + "," + y + "," + z + " )";
}
這裡提供了兩個
構造函式,通過傳遞每個元素的值,或者提供另一個複製其值的Vector,來指定矢量的初始值。第二個構造函式帶一個Vector參數,通常稱為
複製構造函式,因為它們允許通過複製另一個實例來初始化一個類或結構實例。注意,為了簡單起見,把欄位設定為
public。也可以把它們設定為
private,編寫相應的屬性來訪問它們,這樣做不會改變這個程式的功能,只是代碼會複雜一些。
下面是Vector結構的有趣部分—— 為+運算符提供支持的運算符重載:
public static Vector operator + (Vector lhs,Vector rhs)
{
Vector result = new Vector(lhs);
result.x += rhs.x;
result.y += rhs.y;
result.z += rhs.z;
return result;
}
}
}
運算符重載的聲明方式與方法的聲明方式相同,但operator
關鍵字告訴
編譯器,它實際上是一個運算符重載,後面是相關運算符的符號,在本例中就是+。返回類型是在使用這個運算符時獲得的類型。在本例中,把兩個矢量加起來會得到另一個矢量,所以返回類型就是Vector。對於這個+運算符重載,返回類型與包含類一樣,但這種情況並不是必需的。兩個參數就是要操作的對象。對於二元運算符(帶兩個參數),如+和-運算符,第一個參數是放在運算符左邊的值,第二個參數是放在運算符右邊的值。
C#要求所有的運算符重載都聲明為public和
static,這表示它們與它們的類或結構相關聯,而不是與實例相關聯,所以運算符重載的代碼體不能訪問非靜態類成員,也不能訪問this
標識符;這是可以的,因為參數提供了運算符執行任務所需要知道的所有數據。
前面介紹了聲明運算符+的語法,下面看看運算符內部的情況:
{
Vector result = new Vector(lhs);
result.x += rhs.x;
result.y += rhs.y;
result.z += rhs.z;
return result;
}
這部分代碼與聲明方法的代碼是完全相同的,顯然,它返回一個矢量,其中包含前面定義的lhs和rhs的和,即把x、y和z分別相加。
簡單代碼
下面需要編寫一些簡單的代碼,測試Vector結構:
static void Main()
{
Vector vect1,vect2,vect3;
vect1 = new Vector(3.0,3.0,1.0);
vect2 = new Vector(2.0,­­­;–4.0,–4.0);
vect3 = vect1 + vect2;
Console.WriteLine("vect1 = " + vect1.ToString());
Console.WriteLine("vect2 = " + vect2.ToString());
Console.WriteLine("vect3 = " + vect3.ToString());
}
把這些代碼保存為Vectors.cs,
編譯並運行它,結果如下:
Vectors
vect1 = ( 3,3,1 )
vect2 = ( 2,–4,–4 )
vect3 = ( 5,–1,–3 )
運算符重載不能用於Java
下面舉一個TICPP中的例子……
一元的:
//: C12:OverloadingUnaryOperators.cpp
// From Thinking in C++,2nd Edition
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
#include <iostream>
using namespace std;
// Non-member functions:
class Integer {
long i;
Integer* This() { return this; }
public:
Integer(long ll = 0) : i(ll) {}
// No side effects takes const& argument:
friend const Integer&
operator+(const Integer& a);
friend const Integer
operator-(const Integer& a);
friend const Integer
operator~(const Integer& a);
friend Integer*
operator&(Integer& a);
friend int
operator!(const Integer& a);
// Side effects have non-const& argument:
// Prefix:
friend const Integer&
operator++(Integer& a);
// Postfix:
friend const Integer
operator++(Integer& a,int);
// Prefix:
friend const Integer&
operator--(Integer& a);
// Postfix:
friend const Integer
operator--(Integer& a,int);
};
// Global operators:
const Integer& operator+(const Integer& a) {
cout << "+Integer\n";
return a; // Unary + has no effect
}
const Integer operator-(const Integer& a) {
cout << "-Integer\n";
return Integer(-a.i);
}
const Integer operator~(const Integer& a) {
cout << "~Integer\n";
return Integer(~a.i);
}
Integer* operator&(Integer& a) {
cout << "&Integer\n";
return a.This(); // &a is recursive!
}
int operator!(const Integer& a) {
cout << "!Integer\n";
return !a.i;
}
// Prefix; return incremented value
const Integer& operator++(Integer& a) {
cout << "++Integer\n";
a.i++;
return a;
}
// Postfix; return the value before increment:
const Integer operator++(Integer& a,int) {
cout << "Integer++\n";
Integer before(a.i);
a.i++;
return before;
}
// Prefix; return decremented value
const Integer& operator--(Integer& a) {
cout << "--Integer\n";
a.i--;
return a;
}
// Postfix; return the value before decrement:
const Integer operator--(Integer& a,int) {
cout << "Integer--\n";
Integer before(a.i);
a.i--;
return before;
}
// Show that the overloaded operators work:
void f(Integer a) {
+a;
-a;
~a;
Integer* ip = &a;
!a;
++a;
a++;
--a;
a--;
}
// Member functions (implicit "this"):
class Byte {
unsigned char b;
public:
Byte(unsigned char bb = 0) : b(bb) {}
// No side effects: const member function:
const Byte& operator+() const {
cout << "+Byte\n";
return *this;
}
const Byte operator-() const {
cout << "-Byte\n";
return Byte(-b);
}
const Byte operator~() const {
cout << "~Byte\n";
return Byte(~b);
}
Byte operator!() const {
cout << "!Byte\n";
return Byte(!b);
}
Byte* operator&() {
cout << "&Byte\n";
return this;
}
// Side effects: non-const member function:
const Byte& operator++() { // Prefix
cout << "++Byte\n";
b++;
return *this;
}
const Byte operator++(int) { // Postfix
cout << "Byte++\n";
Byte before(b);
b++;
return before;
}
const Byte& operator--() { // Prefix
cout << "--Byte\n";
--b;
return *this;
}
const Byte operator--(int) { // Postfix
cout << "Byte--\n";
Byte before(b);
--b;
return before;
}
};
void g(Byte b) {
+b;
-b;
~b;
Byte* bp = &b;
!b;
++b;
b++;
--b;
b--;
}
int main() {
Integer a;
f(a);
Byte b;
g(b);
} ///:~
二元的:
//: C12:Integer.h
// From Thinking in C++,2nd Edition
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
// Non-member overloaded operators
#ifndef INTEGER_H
#define INTEGER_H
#include <iostream>
// Non-member functions:
class Integer {
long i;
public:
Integer(long ll = 0) : i(ll) {}
// Operators that create new,modified value:
friend const Integer
operator+(const Integer& left,
const Integer& right);
friend const Integer
operator-(const Integer& left,
const Integer& right);
friend const Integer
operator*(const Integer& left,
const Integer& right);
friend const Integer
operator/(const Integer& left,
const Integer& right);
friend const Integer
operator%(const Integer& left,
const Integer& right);
friend const Integer
operator^(const Integer& left,
const Integer& right);
friend const Integer
operator&(const Integer& left,
const Integer& right);
friend const Integer
operator|(const Integer& left,
const Integer& right);
friend const Integer
operator<<(const Integer& left,
const Integer& right);
friend const Integer
operator>>(const Integer& left,
const Integer& right);
// Assignments modify & return lvalue:
friend Integer&
operator+=(Integer& left,
const Integer& right);
friend Integer&
operator-=(Integer& left,
const Integer& right);
friend Integer&
operator*=(Integer& left,
const Integer& right);
friend Integer&
operator/=(Integer& left,
const Integer& right);
friend Integer&
operator%=(Integer& left,
const Integer& right);
friend Integer&
operator^=(Integer& left,
const Integer& right);
friend Integer&
operator&=(Integer& left,
const Integer& right);
friend Integer&
operator|=(Integer& left,
const Integer& right);
friend Integer&
operator>>=(Integer& left,
const Integer& right);
friend Integer&
operator<<=(Integer& left,
const Integer& right);
// Conditional operators return true/false:
friend int
operator==(const Integer& left,
const Integer& right);
friend int
operator!=(const Integer& left,
const Integer& right);
friend int
operator<(const Integer& left,
const Integer& right);
friend int
operator>(const Integer& left,
const Integer& right);
friend int
operator<=(const Integer& left,
const Integer& right);
friend int
operator>=(const Integer& left,
const Integer& right);
friend int
operator&&(const Integer& left,
const Integer& right);
friend int
operator||(const Integer& left,
const Integer& right);
// Write the contents to an ostream:
void print(std::ostream& os) const { os << i; }
};
#endif // INTEGER_H ///:~
//: C12:Integer.cpp {O}
// From Thinking in C++,2nd Edition
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
// Implementation of overloaded operators
#include "Integer.h"
#include "../require.h"
const Integer
operator+(const Integer& left,
const Integer& right) {
return Integer(left.i + right.i);
}
const Integer
operator-(const Integer& left,
const Integer& right) {
return Integer(left.i - right.i);
}
const Integer
operator*(const Integer& left,
const Integer& right) {
return Integer(left.i * right.i);
}
const Integer
operator/(const Integer& left,
const Integer& right) {
require(right.i != 0,"divide by zero");
return Integer(left.i / right.i);
}
const Integer
operator%(const Integer& left,
const Integer& right) {
require(right.i != 0,"modulo by zero");
return Integer(left.i % right.i);
}
const Integer
operator^(const Integer& left,
const Integer& right) {
return Integer(left.i ^ right.i);
}
const Integer
operator&(const Integer& left,
const Integer& right) {
return Integer(left.i & right.i);
}
const Integer
operator|(const Integer& left,
const Integer& right) {
return Integer(left.i | right.i);
}
const Integer
operator<<(const Integer& left,
const Integer& right) {
return Integer(left.i << right.i);
}
const Integer
operator>>(const Integer& left,
const Integer& right) {
return Integer(left.i >> right.i);
}
// Assignments modify & return lvalue:
Integer& operator+=(Integer& left,
const Integer& right) {
if(&left == &right) {/* self-assignment */}
left.i += right.i;
return left;
}
Integer& operator-=(Integer& left,
const Integer& right) {
if(&left == &right) {/* self-assignment */}
left.i -= right.i;
return left;
}
Integer& operator*=(Integer& left,
const Integer& right) {
if(&left == &right) {/* self-assignment */}
left.i *= right.i;
return left;
}
Integer& operator/=(Integer& left,
const Integer& right) {
require(right.i != 0,"divide by zero");
if(&left == &right) {/* self-assignment */}
left.i /= right.i;
return left;
}
Integer& operator%=(Integer& left,
const Integer& right) {
require(right.i != 0,"modulo by zero");
if(&left == &right) {/* self-assignment */}
left.i %= right.i;
return left;
}
Integer& operator^=(Integer& left,
const Integer& right) {
if(&left == &right) {/* self-assignment */}
left.i ^= right.i;
return left;
}
Integer& operator&=(Integer& left,
const Integer& right) {
if(&left == &right) {/* self-assignment */}
left.i &= right.i;
return left;
}
Integer& operator|=(Integer& left,
const Integer& right) {
if(&left == &right) {/* self-assignment */}
left.i |= right.i;
return left;
}
Integer& operator>>=(Integer& left,
const Integer& right) {
if(&left == &right) {/* self-assignment */}
left.i >>= right.i;
return left;
}
Integer& operator<<=(Integer& left,
const Integer& right) {
if(&left == &right) {/* self-assignment */}
left.i <<= right.i;
return left;
}
// Conditional operators return true/false:
int operator==(const Integer& left,
const Integer& right) {
return left.i == right.i;
}
int operator!=(const Integer& left,
const Integer& right) {
return left.i != right.i;
}
int operator<(const Integer& left,
const Integer& right) {
return left.i < right.i;
}
int operator>(const Integer& left,
const Integer& right) {
return left.i > right.i;
}
int operator<=(const Integer& left,
const Integer& right) {
return left.i <= right.i;
}
int operator>=(const Integer& left,
const Integer& right) {
return left.i >= right.i;
}
int operator&&(const Integer& left,
const Integer& right) {
return left.i && right.i;
}
int operator||(const Integer& left,
const Integer& right) {
return left.i || right.i;
} ///:~
//: C12:IntegerTest.cpp
// From Thinking in C++,2nd Edition
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
//{L} Integer
#include "Integer.h"
#include <fstream>
using namespace std;
ofstream out("IntegerTest.out");
void h(Integer& c1,Integer& c2) {
// A complex expression:
c1 += c1 * c2 + c2 % c1;
#define TRY(OP) \
out << "c1 = "; c1.print(out); \
out << ",c2 = "; c2.print(out); \
out << "; c1 " #OP " c2 produces "; \
(c1 OP c2).print(out); \
out << endl;
TRY(+) TRY(-) TRY(*) TRY(/)
TRY(%) TRY(^) TRY(&) TRY(|)
TRY(<<) TRY(>>) TRY(+=) TRY(-=)
TRY(*=) TRY(/=) TRY(%=) TRY(^=)
TRY(&=) TRY(|=) TRY(>>=) TRY(<<=)
// Conditionals:
#define TRYC(OP) \
out << "c1 = "; c1.print(out); \
out << ",c2 = "; c2.print(out); \
out << "; c1 " #OP " c2 produces "; \
out << (c1 OP c2); \
out << endl;
TRYC(<) TRYC(>) TRYC(==) TRYC(!=) TRYC(<=)
TRYC(>=) TRYC(&&) TRYC(||)
}
int main() {
cout << "friend functions" << endl;
Integer c1(47),c2(9);
h(c1,c2);
} ///:~
基本模型
終止模型
一種稱為"終止模型"(它是Java與C++所支持的模型).在這種模型中,將假設錯誤非常關鍵,將以致於程式無法返回到異常發生的地方繼續執行.一旦異常被拋出,就表明錯誤已無法挽回,也不能回來繼續執行.
恢復模型
另一種稱為"恢復模型".意思是異常處理程式的工作是修正錯誤,然後重新嘗試調動出問題的方法,並認為的二次能成功. 對於恢復模型,通常希望異常被處理之後能繼續執行程式.在這種情況下,拋出異常更像是對方法的調用--可以在Java里用這種方法進行配置,以得到類似恢復的行為.(也就是說,不是拋出異常,而是調用方法修正錯誤.)或者,把try塊放在while循環里,這樣就可以不斷的進入try塊,直到得到滿意的結果.
各有千秋
雖然恢復模型開始顯得很吸引人,並且人們使用的作業系統也支持恢復模型的異常處理,但程式設計師們最終還是轉向了使用類似"終止模型"的代碼.因為:處理程式必須關注異常拋出的地點,這勢必要包含依賴於拋出位置的非通用性代碼.這增加了代碼編寫和維護的困難,對於異常可能會從許多地方拋出的大型程式來說,更是如此. 下面我寫的一個簡單的例子 VC++6.0下通過
#include <iostream>
using namespace std;
class Error
{
public:
virtual void show()=0;
};
class DenoError:public Error
{
public:
void show() { cout<<"分母不可以為0!"<<endl; }
};
void main()
{
int a,b;
cin>>a>>b;
try {
DenoError e;
if(b==0) throw e;
int c=a/b;
cout<<c<<endl;
}
catch(DenoError & e)
{ e.show(); }
}
分類
支持運算符重載和定義新運算符的語言:
PostgreSQL的SQL方言
Ruby
Haskell
支持運算符重載的語言:
不支持運算符重載的語言: