簡體中文版翻譯:申?F,nicrosoft@sunistudio.com(東日制作室,東日文檔)
它允許你為類的用戶提供一個直覺的接口。 算符重載允許C/C++的運算符在用戶定義類型(類)上擁有一個用戶定義的意義。重載的算符是函數調用的語法修飾:
class Fred {
public:
// ...
};
#if 0
// 沒有算符重載:
Fred add(Fred, Fred);
Fred mul(Fred, Fred);
Fred f(Fred a, Fred b, Fred c)
{
return add(add(mul(a,b), mul(b,c)), mul(c,a)); // 哈哈,多可笑...
}
#else
// 有算符重載:
Fred operator+ (Fred, Fred);
Fred operator* (Fred, Fred);
Fred f(Fred a, Fred b, Fred c)
{
return a*b + b*c + c*a;
}
#endif
通過重載類上的標準算符,你可以發掘類的用戶的直覺。使得用戶程序所用的語言是面向問題的,而不是面向機器的。 最終目標是降低學習曲線并減少錯誤率。 [ Top | Bottom | Previous section | Next section ]
[Recently changed so it uses the std:: syntax (on 7/00). Click here to go to the next FAQ in the "chain" of recent changes.] 這里有一些算符重載的實例:
算符重載使得類的用戶的工作更簡易,而不是為類的開發者服務的! 考慮一下如下的例子:
class Array {
public:
int& operator[] (unsigned i); // 有些人不喜歡這種語法
// ...
};
inline
int& Array::operator[] (unsigned i) // 有些人不喜歡這種語法
{
// ...
}
int main()
{
Array a;
a[3] = 4; // 用戶代碼應該明顯而且易懂...
}
大多數都可以被重載。C的算符中只有 . 和 ? :(以及sizeof,技術上可以看作一個算符)。C++增加了一些自己的算符,除了::和.*,大多數都可以被重載。 這是一個下標算符的示例(它返回一個引用)。先沒有算符重載:
class Array {
public:
int& elem(unsigned i)/t{ if (i > 99) error(); return data; }
private:
int data[100];
};
int main()
{
Array a;
a.elem(10) = 42;
a.elem(12) += a.elem(13);
}
class Array {
public:
int& operator[] (unsigned i) { if (i > 99) error(); return data; }
private:
int data[100];
};
int main()
{
Array a;
a[10] = 42;
a[12] += a[13];
}
[Recently changed so it uses the std:: syntax (on 7/00). Click here to go to the next FAQ in the "chain" of recent changes.] 不行:被重載的算符,至少一個操作數必須是用戶定義類型(大多數時候是類)。 但即使C++允許,也不要這樣做。因為在此處你應該使用類似 std::string的類而不是字符數組,因為數組是有害的。因此無論如何你都不會想那樣做的。 [ Top | Bottom | Previous section | Next section ]
不行。 運算符的名稱、優先級、結合性以及元數都是由語言固定的。在C++中沒有operator**,因此你不能為類類型創建它。 如果還有疑問,考慮一下x ** y與x * (*y)等同(換句話說,編譯器假定 y 是一個指針)。此外,算符重載只不過是函數調用的語法修飾。雖然這種特殊的語法修飾非常美妙,但它沒有增加任何本質的東西。我建議你重載pow(base,exponent)(雙精度版本在<cmath>中)。 順便提一下,operator^可以成為冪運算,只是優先級和結合性是錯誤的。 [ Top | Bottom | Previous section | Next section ]
[Recently changed so it uses new-style headers and the std:: syntax (on 7/00). Click here to go to the next FAQ in the "chain" of recent changes.] 用 operator()而不是operator[]。 當有多個下標時,最清晰的方式是使用operator()而不是operator[]。原因是operator[]總是帶一個參數,而operator()可以帶任何數目的參數(在矩形的矩陣情況下,需要兩個參數)。 如:
class Matrix {
public:
Matrix(unsigned rows, unsigned cols);
double& operator() (unsigned row, unsigned col);
double operator() (unsigned row, unsigned col) const;
// ...
~Matrix();/t/t/t // 析構函數
Matrix(const Matrix& m);/t // 拷貝構造函數
Matrix& operator= (const Matrix& m); // 賦值算符
// ...
private:
unsigned rows_, cols_;
double* data_;
};
inline
Matrix::Matrix(unsigned rows, unsigned cols)
: rows_ (rows),
cols_ (cols),
data_ (new double[rows * cols])
{
if (rows == 0 || cols == 0)
throw BadIndex("Matrix constructor has 0 size");
}
inline
Matrix::~Matrix()
{
delete[] data_;
}
inline
double& Matrix::operator() (unsigned row, unsigned col)
{
if (row >= rows_ || col >= cols_)
throw BadIndex("Matrix subscript out of bounds");
return data_[cols_*row + col];
}
inline
double Matrix::operator() (unsigned row, unsigned col) const
{
if (row >= rows_ || col >= cols_)
throw BadIndex("const Matrix subscript out of bounds");
return data_[cols_*row + col];
}
int main()
{
Matrix m(10,10);
m(5,8) = 106.15;
std::cout << m(5,8);
// ...
}
本 FAQ 其實是關于:某些人建立的Matrix 類,帶有一個返回 Array 對象的引用的operator[]。而該Array 對象也帶有一個 operator[] ,它返回Matrix的一個元素(例如,一個double的引用)。因此,他們使用類似m[j] 的語法來訪問矩陣的元素,而不是象m(i,j)的語法。 數組的數組方案顯然可以工作,但相對于operator()方法來說,缺乏靈活性。尤其是,用[][]方法很難表現的時候,用operator()方法可以很簡單的完成,因此[][]方法很可能導致差勁的表現,至少某些情況細是這樣的。 例如,實現[][]方法的最簡單途徑就是使用作為密集矩陣的,以以行為主的形式保存(或以列為主,我記不清了)的物理布局。相反,operator() 方法完全隱藏了矩陣的物理布局,在這種情況下,它可能帶來更好的表現。 可以這么認為:operator()方法永遠不比[][]方法差,有時更好。
[Recently changed so it uses new-style headers and the std:: syntax and reworded references to STL (on 7/00). Click here to go to the next FAQ in the "chain" of recent changes.] 從外部! 良好的接口提供了一個簡化的,以用戶詞匯表達的視圖。在面向對象軟件的情況下,接口通常是單個類或一組緊密結合的類的public方法的集合. 首先考慮對象的邏輯特征是什么,而不是打算如何創建它。例如,假設要創建一個Stack(棧)類,其包含一個 LinkedList:
class Stack {
public:
// ...
private:
LinkedList list_;
};
class Node { /*...*/ };
class LinkedList {
public:
// ...
private:
Node* first_;
};
void userCode(LinkedList& a)
{
for (LinkedListIterator p = a.begin(); p != a.end(); ++p)
std::cout << *p << '';
}
#include <cassert> // Poor man's exception handling
class LinkedListIterator;
class LinkedList;
class Node {
// No public members; this is a "private class"
friend LinkedListIterator; // 友員類
friend LinkedList;
Node* next_;
int elem_;
};
class LinkedListIterator {
public:
bool operator== (LinkedListIterator i) const;
bool operator!= (LinkedListIterator i) const;
void operator++ (); // Go to the next element
int& operator* (); // Access the current element
private:
LinkedListIterator(Node* p);
Node* p_;
friend LinkedList; // so LinkedList can construct a LinkedListIterator
};
class LinkedList {
public:
void append(int elem); // Adds elem after the end
void prepend(int elem); // Adds elem before the beginning
// ...
LinkedListIterator begin();
LinkedListIterator end();
// ...
private:
Node* first_;
};
inline bool LinkedListIterator::operator== (LinkedListIterator i) const
{
return p_ == i.p_;
}
inline bool LinkedListIterator::operator!= (LinkedListIterator i) const
{
return p_ != i.p_;
}
inline void LinkedListIterator::operator++()
{
assert(p_ != NULL); // or if (p_==NULL) throw ...
p_ = p_->next_;
}
inline int& LinkedListIterator::operator*()
{
assert(p_ != NULL); // or if (p_==NULL) throw ...
return p_->elem_;
}
inline LinkedListIterator::LinkedListIterator(Node* p)
: p_(p)
{ }
inline LinkedListIterator LinkedList::begin()
{
return first_;
}
inline LinkedListIterator LinkedList::end()
{
return NULL;
}
新聞熱點
疑難解答
圖片精選