简体中文版翻译:申?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;
}
新闻热点
疑难解答
图片精选