📄 c++ faq lite[13]--算符重载(新).htm
字号:
| <A
href="http://www.sunistudio.com/cppfaq/friends.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[13.7]></A>
<DIV class=FaqTitle>
<H3>[13.7] 我能为“幂”运算创建一个 <TT><FONT face=新宋体>operator**</FONT></TT> 吗?</H3></DIV>
<P>不行。
<P>运算符的名称、优先级、结合性以及元数都是由语言固定的。在C++中没有<TT><FONT
face=新宋体>operator**</FONT></TT>,因此你不能为类类型创建它。
<P>如果还有疑问,考虑一下<TT><FONT face=新宋体>x ** y</FONT></TT>与<TT><FONT
face=新宋体>x * (*y)</FONT></TT>等同(换句话说,编译器假定 <TT><FONT face=新宋体>y
</FONT></TT>是一个指针)。此外,算符重载只不过是函数调用的语法修饰。虽然这种特殊的语法修饰非常美妙,但它没有增加任何本质的东西。我建议你重载<TT><FONT
face=新宋体>pow(base,exponent)</FONT></TT>(双精度版本在<TT><FONT
face=新宋体><cmath></FONT></TT>中)。
<P>顺便提一下,<TT><FONT face=新宋体>operator^</FONT></TT>可以成为幂运算,只是优先级和结合性是错误的。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html#top">Top</A>
| <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html#bottom">Bottom</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/friends.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[13.8]></A>
<DIV class=FaqTitle>
<H3>[13.8] 如何为<FONT face=新宋体><TT>Matrix</TT><TT>(</TT></FONT>矩阵)类创建下标运算符? <IMG
alt=UPDATED!
src="C++ FAQ Lite[13]--算符重载(新).files/updated.gif"></H3></DIV><SMALL><EM>[Recently
changed so it uses new-style headers and the <TT><FONT
face=新宋体>std::</FONT></TT> syntax (on 7/00). <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html#[13.10]">Click
here to go to the next FAQ in the "chain" of recent changes<!--rawtext:[13.10]:rawtext--></A>.]</EM></SMALL>
<P>用 <TT><FONT face=新宋体>operator()</FONT></TT>而不是<TT><FONT
face=新宋体>operator[]</FONT></TT>。
<P>当有多个下标时,最清晰的方式是使用<TT><FONT face=新宋体>operator()</FONT></TT>而不是<TT><FONT
face=新宋体>operator[]</FONT></TT>。原因是<TT><FONT
face=新宋体>operator[]</FONT></TT>总是带一个参数,而<TT><FONT
face=新宋体>operator()</FONT></TT>可以带任何数目的参数(在矩形的矩阵情况下,需要两个参数)。
<P>如:
<P>
<DIV class=CodeBlock><TT><FONT
face=新宋体> class Matrix {<BR> public:<BR> Matrix(unsigned rows, unsigned cols);<BR> double& operator() (unsigned row, unsigned col);<BR> double operator() (unsigned row, unsigned col) const;<BR> </FONT></TT><EM>// ...</EM><TT><BR><FONT
face=新宋体> ~Matrix(); </FONT></TT><EM>// 析构函数</EM><TT><BR><FONT
face=新宋体> Matrix(const Matrix& m); </FONT></TT><EM>// 拷贝构造函数</EM><TT><BR><FONT
face=新宋体> Matrix& operator= (const Matrix& m); </FONT></TT><EM>// 赋值算符</EM><TT><BR><FONT
face=新宋体> </FONT></TT><EM>// ...</EM><TT><BR><FONT
face=新宋体> private:<BR> unsigned rows_, cols_;<BR> double* data_;<BR> };<BR> <BR> inline<BR> Matrix::Matrix(unsigned rows, unsigned cols)<BR> : rows_ (rows),<BR> cols_ (cols),<BR> data_ (new double[rows * cols])<BR> {<BR> if (rows == 0 || cols == 0)<BR> throw BadIndex("Matrix constructor has 0 size");<BR> }<BR> <BR> inline<BR> Matrix::~Matrix()<BR> {<BR> delete[] data_;<BR> }<BR> <BR> inline<BR> double& Matrix::operator() (unsigned row, unsigned col)<BR> {<BR> if (row >= rows_ || col >= cols_)<BR> throw BadIndex("Matrix subscript out of bounds");<BR> return data_[cols_*row + col];<BR> }<BR> <BR> inline<BR> double Matrix::operator() (unsigned row, unsigned col) const<BR> {<BR> if (row >= rows_ || col >= cols_)<BR> throw BadIndex("const Matrix subscript out of bounds");<BR> return data_[cols_*row + col];<BR> }
</FONT></TT></DIV>
<P>然后,你可以使用<TT><FONT face=新宋体>m(i,j)</FONT></TT>来访问<TT><FONT
face=新宋体>Matrix</FONT></TT> <TT><FONT face=新宋体>m</FONT></TT> 的元素,而不是<TT><FONT
face=新宋体>m[i][j]:</FONT></TT>
<P>
<DIV class=CodeBlock><TT><FONT
face=新宋体> int main()<BR> {<BR> Matrix m(10,10);<BR> m(5,8) = 106.15;<BR> std::cout << m(5,8);<BR> </FONT></TT><EM>// ...</EM><TT><BR><FONT
face=新宋体> } </FONT></TT></DIV>
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html#top">Top</A>
| <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html#bottom">Bottom</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/friends.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[13.9]></A>
<DIV class=FaqTitle>
<H3>[13.9] 为什么<TT><FONT face=新宋体>Matrix</FONT></TT>(矩阵)类的接口不应该象数组的数组?</H3></DIV>
<P>本 FAQ 其实是关于:某些人建立的Matrix 类,带有一个返回 <TT><FONT face=新宋体>Array</FONT></TT>
对象的引用的<TT><FONT face=新宋体>operator[]</FONT></TT>。而该<TT><FONT
face=新宋体>Array</FONT></TT> 对象也带有一个 <TT><FONT face=新宋体>operator[]</FONT></TT>
,它返回Matrix的一个元素(例如,一个<TT><FONT
face=新宋体>double</FONT></TT>的引用)。因此,他们使用类似<TT><FONT face=新宋体>m[i][j]</FONT></TT>
的语法来访问矩阵的元素,而不是<A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html#[13.8]">象<TT><FONT
face=新宋体>m(i,j)</FONT></TT>的语法</A>。
<P>数组的数组方案显然可以工作,但相对于<A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html#[13.8]"><TT><FONT
face=新宋体>operator()</FONT></TT>方法</A>来说,缺乏灵活性。尤其是,用<TT><FONT
face=新宋体>[][]</FONT></TT>方法很难表现的时候,用<TT><FONT
face=新宋体>operator()</FONT></TT>方法可以很简单的完成,因此<TT><FONT
face=新宋体>[][]</FONT></TT>方法很可能导致差劲的表现,至少某些情况细是这样的。
<P>例如,实现<TT><FONT
face=新宋体>[][]</FONT></TT>方法的最简单途径就是使用作为密集矩阵的,以以行为主的形式保存(或以列为主,我记不清了)的物理布局。相反,<A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html#[13.8]"><TT><FONT
face=新宋体>operator()</FONT></TT> 方法</A>完全隐藏了矩阵的物理布局,在这种情况下,它可能带来更好的表现。
<P>可以这么认为:<TT><FONT face=新宋体>operator()</FONT></TT>方法永远不比<TT><FONT
face=新宋体>[][]</FONT></TT>方法差,有时更好。
<UL>
<LI><TT><FONT face=新宋体>operator()</FONT></TT> 永远不差,是因为用<TT><FONT
face=新宋体>operator()</FONT></TT>方法实现以行为主的密集矩阵的物理布局非常容易。因此,当从性能观点出发,那样的结构正好是最佳布局时,<TT><FONT
face=新宋体>operator()</FONT></TT>方法也和<TT><FONT
face=新宋体>[][]</FONT></TT>方法一样简单(也许<TT><FONT
face=新宋体>operator()</FONT></TT>方法更容易一点点,但我不想夸大其词)。
<LI><FONT face=新宋体><TT>operator()</TT><TT>
</TT></FONT>方法有时更好,是因为当对于给定的应用,有其它比以行为主的密集矩阵更好的布局时,用 <TT><FONT
face=新宋体>operator()</FONT></TT> 方法比<TT><FONT
face=新宋体>[][]</FONT></TT>方法实现会容易得多。 </LI></UL>
<P>作为一个物理布局使得实现困难的例子,最近的项目发生在以列访问矩阵元素(也就是,算法访问一列中的所有元素,然后是另一列等),如果物理布局是以行为主的,对矩阵的访问可能会“cache失效”。例如,如果行的大小几乎和处理器的cache大小相当,那么对每个元素的访问,都会发生“cache不命中”。在这个特殊的项目中,我们通过将映射从逻辑布局(行,列)变为物理布局(列,行),性能得到了20%的提升。
<P>当然,还有很多这类事情的例子,而稀疏矩阵在这个问题中则是又一类例子。通常,使用<TT><FONT
face=新宋体>operator()</FONT></TT>方法实现一个稀疏矩阵或交换行/列顺序更容易,<TT><FONT
face=新宋体>operator()</FONT></TT>方法不会损失什么,而可能获得一些东西——它不会更差,却可能更好。
<P>使用 <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html#[13.8]"><TT><FONT
face=新宋体>operator()</FONT></TT> 方法</A>。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html#top">Top</A>
| <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html#bottom">Bottom</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/friends.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[13.10]></A>
<DIV class=FaqTitle>
<H3>[13.10] 该从外(接口优先)还是从内(数据优先)设计类?<IMG alt=UPDATED!
src="C++ FAQ Lite[13]--算符重载(新).files/updated.gif"></H3></DIV><SMALL><EM>[Recently
changed so it uses new-style headers and the <TT><FONT
face=新宋体>std::</FONT></TT> syntax and reworded references to STL (on 7/00). <A
href="http://www.sunistudio.com/cppfaq/friends.html#[14.2]">Click here to go to
the next FAQ in the "chain" of recent
changes<!--rawtext:[14.2]:rawtext--></A>.]</EM></SMALL>
<P>从外部!
<P>良好的接口提供了一个<A
href="http://www.sunistudio.com/cppfaq/classes-and-objects.html#[7.3]">简化的,以用户词汇表达的视图</A>。在面向对象软件的情况下,接口通常是单个类或<A
href="http://www.sunistudio.com/cppfaq/friends.html#[14.2]">一组紧密结合的类</A>的public方法的集合.
<P>首先考虑对象的逻辑特征是什么,而不是打算如何创建它。例如,假设要创建一个<TT><FONT
face=新宋体>Stack</FONT></TT>(栈)类,其包含一个 <TT><FONT face=新宋体>LinkedList</FONT></TT>:
<P>
<DIV class=CodeBlock><TT><FONT
face=新宋体> class Stack {<BR> public:<BR> </FONT></TT><EM>// ...</EM><TT><BR><FONT
face=新宋体> private:<BR> LinkedList list_;<BR> };
</FONT></TT></DIV>
<P>Stack是否应该有一个返回<TT><FONT face=新宋体>LinkedList</FONT></TT>的<TT><FONT
face=新宋体>get()</FONT></TT>方法?或者一个带有<TT><FONT
face=新宋体>LinkedList</FONT></TT>的<TT><FONT
face=新宋体>set()</FONT></TT>方法?或者一个带有<TT><FONT
face=新宋体>LinkedList</FONT></TT>的构造函数?显然,答案是“不”,因为应该从外向里设计接口。也就是说,<TT><FONT
face=新宋体>Stack</FONT></TT>对象的用户并不关心 <TT><FONT
face=新宋体>LinkedList</FONT></TT>;他们只关心 pushing 和 popping。
<P>现在看另一个更微妙的例子。假设 <TT><FONT face=新宋体>LinkedList</FONT></TT>类使用<TT><FONT
face=新宋体>Node</FONT></TT>对象的链表来创建,每一个<TT><FONT
face=新宋体>Node</FONT></TT>对象有一个指向下一个<TT><FONT face=新宋体>Node</FONT></TT>的指针:
<P>
<DIV class=CodeBlock><TT><FONT
face=新宋体> class Node { </FONT></TT><EM>/*...*/</EM><TT><FONT
face=新宋体> };<BR> <BR> class LinkedList {<BR> public:<BR> </FONT></TT><EM>// ...</EM><TT><BR><FONT
face=新宋体> private:<BR> Node* first_;<BR> };
</FONT></TT></DIV>
<P><TT><FONT face=新宋体>LinkedList</FONT></TT>类是否应该有一个让用户访问第一个<TT><FONT
face=新宋体>Node</FONT></TT>的<TT><FONT face=新宋体>get()</FONT></TT>方法?<FONT
face=新宋体><TT>Node</TT><TT> </TT></FONT>对象是否应该有一个让用户访问链中下一个 <FONT
face=新宋体><TT>Node</TT><TT> </TT></FONT>的 <TT><FONT
face=新宋体>get()</FONT></TT>方法?换句话说,从外部看,<TT><FONT
face=新宋体>LinkedList</FONT></TT>应该是什么样的?<TT><FONT face=新宋体>LinkedList
</FONT></TT>是否实际上就是一个 <TT><FONT face=新宋体>Node
</FONT></TT>对象的链?或者这些只是实现的细节?如果只是实现的细节,<TT><FONT face=新宋体>LinkedList
</FONT></TT>将如何让用户在某时刻访问 <TT><FONT face=新宋体>LinkedList </FONT></TT>中的每一个元素?
<P>某人的回答:<TT><FONT face=新宋体>LinkedList </FONT></TT>不是的 <TT><FONT face=新宋体>Node
</FONT></TT>链。它可能的确是用 <TT><FONT face=新宋体>Node
</FONT></TT>创建的,但这不是本质。它的本质是元素的序列。因此,<TT><FONT face=新宋体>LinkedList</FONT></TT>
抽象应该提供一个“LinkedListIterator”,并且“LinkedListIterator”应该有一个<TT><FONT
face=新宋体>operator++</FONT></TT> 来访问下一个元素,并且有一对<TT><FONT
face=新宋体>get()</FONT></TT>/<TT><FONT face=新宋体>set()</FONT></TT>来访问存储于<TT><FONT
face=新宋体>Node</FONT></TT> 的值(<TT><FONT face=新宋体>Node</FONT></TT>
元素中的值只由<TT><FONT face=新宋体>LinkedList</FONT></TT>用户负责,因此有一对<TT><FONT
face=新宋体>get()</FONT></TT>/<TT><FONT face=新宋体>set()</FONT></TT>以允许用户自由地维护该值)。
<P>从用户的观点出发,我们可能希望 <TT><FONT
face=新宋体>LinkedList</FONT></TT>类支持看上去类似使用指针算法访问数组的算符:
<P>
<DIV class=CodeBlock><TT><FONT
face=新宋体> void userCode(LinkedList& a)<BR> {<BR> for (LinkedListIterator p = a.begin(); p != a.end(); ++p)<BR> std::cout << *p << '\n';<BR> }
</FONT></TT></DIV>
<P>实现这个接口,<TT><FONT face=新宋体>LinkedList</FONT></TT>需要一个 <TT><FONT
face=新宋体>begin()</FONT></TT>方法和 <TT><FONT
face=新宋体>end()</FONT></TT>方法。它们返回一个“LinkedListIterator”对象。该“LinkedListIterator”需要一个前进的方法,<TT><FONT
face=新宋体>++p</FONT></TT> ;访问当前元素的方法,<TT><FONT
face=新宋体>*p</FONT></TT>;和一个比较算符,<TT><FONT
face=新宋体>p != a.end()</FONT></TT>。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -