一个看起来奇怪的C++程序 && c++操作符重载
2012-06-18 19:57
471 查看
这篇随笔源自在coolshell.cn上看到的一篇文章,也第一次看到这样的话,这是linux之父linus说的,在解释他为什么不用C++来写git :
c++ is a horrible language, it is made more horrible by the fact that a lot of substandard programmers use it.
自己看书有很多大神级别的人是这种观点,比如好像the art of unix programming的作者是, 好像很多写程序的高手都认同这样的想法,像云风,coolshell的博主陈皓,陈皓表达了C++是一个不成熟的语言的观点。先不说这个,看一下下面这个程序:
1, 一个看起来奇怪的c++程序
让人觉得诡异的是42行, 运行结果是这样的
Hack constructor : name = vector
Hack constructor : name = UINT4
Hack constructor : name = foo
vector小于UINT4
vector大于foo
vector小于UINT4
vector大于foo
事实上当把程序整个看一下之后明白,vector<UINT4> foo;并不是我们第一感觉的那样,当然也不应该这么去写,但是这里只是想说C++可以去这么做到。42行实际上是43行的样子,从运行的结果也可以看出来,怎么解释这个现象呢,现在我只能简单的理解为,编译器在main函数这个域中找到了vector, UINT4,foo的定义,它们都是Hack类的对象,于是就没有再往其它namespace中去寻找了(没有去std::中找vector,也没有在::中找UINT4的typedef定义), 然后再根据这个语句的结构去找operator<的重载定义,而且operator<的返回结果能够继续下面的调用,就通过了。 当我把operator>定义(31至34行)改成如下,运行结果没有变化, 当然改operator<就不行了
2. 比较让我感到奇怪的是,也是要注意的是,当把上面程序的注释拿掉,也就是给operator<, operator>再定义一个member function的版本时,在42行,也就是调用operator<的时候就会出错,给出的信息是 :
42:10: error: ambiguous overload for ‘operator<’ in ‘vector < UINT4’ 。
也就是全局和成员的 operator<定义导至了岐义,可以简单理解为编译器为一个operator(至少两元)寻找定义时(当然这时候operator的调用是以纯operator的形式),会搜寻左操作数类定义的域和全局域,还有所有引入的命名空间的域,若发现有不只一个匹配的,就报错了, 为了测试,我把全局的operator定义放到了 namespace wy下, 并且在main如下修改,在如下的位置加入第2行
给出的输出结果是:
vector小于UINT4(memeber version)
vector大于foo(memeber version)
vector小于UINT4
vector大于foo
vector小于UINT4(memeber version)
vector大于foo(memeber version)
如果把 usingnamespace wy;放在第1行之前, 也会报上面的ambiguous的错,可见当使用 vector<UNIT4这样语句时会搜寻所有引的命名空间, 而从下面4行的输出可以看出当把operator重载的形式当全局函数或类的成员函数的形式来直接调用,是可以的,也不会有岐义
3. 操作符重载, memebers or namespace functions
上面的例子中其实是把重载的< 和 > 定义为 namespace function的,那到底一般怎么选择,我看了一下thinking in C++和C++ primer中说的,下面这张图摘自 thinking in c++ , 12章
先说说后两行,推荐为member的二元operator,第三行,全是赋值性的。 当然可以看到, < , > 都推荐实现为non-member
第二行的几个operator都必须实现为member, 我自己写测试程序,发现编译器对 =, [ ]的要求是只能带一个参数,可以是任意的, 对->的要求是不能有参数(must be void), operator()我在之前一篇随笔中验证过了,可以任意参数。 对于返回值,都没有要求,但是实现为member的operator的返回都应该是类对象本身的引用,即返回this,因为可以在一个表达式中用多个operator, 下面是在类Hack中加上的,都没有报错
关于friend, 可以把一个函数声明为一个类的friend, 这个函数既可以是member function, 也可以是namespace function, 这样这个函数中就可以直接访问这个类所有对象的private member了, friend最主要的用途是在operator overloading中, 比如我要给Hack类定义operator << 和operator>>, 配合IOStream的输入输出操作对一个类来说是很常用的,当然这不能定义为member了(想想为什么?因为只会去操作符左操作数的类定义中去搜寻相关operator的定义),在class Hack中如下声明
然后再到全局域中去定义,全局域中定义的时候不能加上friend了,friend这个关健词只能出现在class中
c++ is a horrible language, it is made more horrible by the fact that a lot of substandard programmers use it.
自己看书有很多大神级别的人是这种观点,比如好像the art of unix programming的作者是, 好像很多写程序的高手都认同这样的想法,像云风,coolshell的博主陈皓,陈皓表达了C++是一个不成熟的语言的观点。先不说这个,看一下下面这个程序:
1, 一个看起来奇怪的c++程序
#include <iostream> #include <vector> #include <string> typedef int UINT4; using namespace std; class Hack { public : Hack(string name) :name_(name){ cout << "Hack constructor : name = " << name_ << endl; } string name(){ return name_; } /*Hack& operator< (Hack& a){ cout << name_ << "小于" << a.name() << "(memeber version)" << endl; return *this; } Hack& operator> (Hack& a){ cout << name_ << "大于" << a.name() << "(memeber version)" << endl; return *this; }*/ private : string name_; }; Hack& operator< (Hack &a , Hack &b) { std::cout << a.name() << "小于" << b.name() << endl; return a; } Hack& operator> (Hack &a, Hack &b) { std::cout << a.name() << "大于" << b.name() << endl; return a; } int main(int argc, char ** argv) { Hack vector("vector"); Hack UINT4("UINT4"); Hack foo("foo"); vector<UINT4> foo; operator>(operator<(vector, UINT4), foo); // vector.operator<(UINT4).operator>(foo); return(0); }
让人觉得诡异的是42行, 运行结果是这样的
Hack constructor : name = vector
Hack constructor : name = UINT4
Hack constructor : name = foo
vector小于UINT4
vector大于foo
vector小于UINT4
vector大于foo
事实上当把程序整个看一下之后明白,vector<UINT4> foo;并不是我们第一感觉的那样,当然也不应该这么去写,但是这里只是想说C++可以去这么做到。42行实际上是43行的样子,从运行的结果也可以看出来,怎么解释这个现象呢,现在我只能简单的理解为,编译器在main函数这个域中找到了vector, UINT4,foo的定义,它们都是Hack类的对象,于是就没有再往其它namespace中去寻找了(没有去std::中找vector,也没有在::中找UINT4的typedef定义), 然后再根据这个语句的结构去找operator<的重载定义,而且operator<的返回结果能够继续下面的调用,就通过了。 当我把operator>定义(31至34行)改成如下,运行结果没有变化, 当然改operator<就不行了
void operator> (Hack &a, Hack &b) { std::cout << a.name() << "大于" << b.name() << endl; // return a; }
2. 比较让我感到奇怪的是,也是要注意的是,当把上面程序的注释拿掉,也就是给operator<, operator>再定义一个member function的版本时,在42行,也就是调用operator<的时候就会出错,给出的信息是 :
42:10: error: ambiguous overload for ‘operator<’ in ‘vector < UINT4’ 。
也就是全局和成员的 operator<定义导至了岐义,可以简单理解为编译器为一个operator(至少两元)寻找定义时(当然这时候operator的调用是以纯operator的形式),会搜寻左操作数类定义的域和全局域,还有所有引入的命名空间的域,若发现有不只一个匹配的,就报错了, 为了测试,我把全局的operator定义放到了 namespace wy下, 并且在main如下修改,在如下的位置加入第2行
vector<UINT4> foo; using namespace wy; operator>(operator<(vector, UINT4), foo); vector.operator<(UINT4).operator>(foo);
给出的输出结果是:
vector小于UINT4(memeber version)
vector大于foo(memeber version)
vector小于UINT4
vector大于foo
vector小于UINT4(memeber version)
vector大于foo(memeber version)
如果把 usingnamespace wy;放在第1行之前, 也会报上面的ambiguous的错,可见当使用 vector<UNIT4这样语句时会搜寻所有引的命名空间, 而从下面4行的输出可以看出当把operator重载的形式当全局函数或类的成员函数的形式来直接调用,是可以的,也不会有岐义
3. 操作符重载, memebers or namespace functions
上面的例子中其实是把重载的< 和 > 定义为 namespace function的,那到底一般怎么选择,我看了一下thinking in C++和C++ primer中说的,下面这张图摘自 thinking in c++ , 12章
先说说后两行,推荐为member的二元operator,第三行,全是赋值性的。 当然可以看到, < , > 都推荐实现为non-member
第二行的几个operator都必须实现为member, 我自己写测试程序,发现编译器对 =, [ ]的要求是只能带一个参数,可以是任意的, 对->的要求是不能有参数(must be void), operator()我在之前一篇随笔中验证过了,可以任意参数。 对于返回值,都没有要求,但是实现为member的operator的返回都应该是类对象本身的引用,即返回this,因为可以在一个表达式中用多个operator, 下面是在类Hack中加上的,都没有报错
void operator->(void){ } int operator[](string str){ } void operator=(float f){ }
关于friend, 可以把一个函数声明为一个类的friend, 这个函数既可以是member function, 也可以是namespace function, 这样这个函数中就可以直接访问这个类所有对象的private member了, friend最主要的用途是在operator overloading中, 比如我要给Hack类定义operator << 和operator>>, 配合IOStream的输入输出操作对一个类来说是很常用的,当然这不能定义为member了(想想为什么?因为只会去操作符左操作数的类定义中去搜寻相关operator的定义),在class Hack中如下声明
class Hack{ friend ostream& operator<<(ostream& os, const Hack& hack); friend istream& operator>>(ostream& is, Hack& hack){} } ostream& operator<<(ostream& os, const Hack& hack){}
然后再到全局域中去定义,全局域中定义的时候不能加上friend了,friend这个关健词只能出现在class中
相关文章推荐
- C++中重载数组下标访问操作符[ ] 和 赋值操作符 = ,* 和 -> 操作符和取反 !以及转化操作符bool和void*
- c++重载io输入输出操作符的一个简单例子
- C/C++—— 一个特别奇怪的C++程序
- More Effective C++ 条款7 千万不要重载&&,||和,操作符
- 一个C&C++程序的生命历程
- C++中输入输出<< 和>>重载,以便适应输出输入一个对象
- c++输出(<<)操作符重载
- 一个C&C++程序的生命历程
- 从零开始C++ 第二课 CC++初体验,编写一个简单的程序·hello C++ 课程一天第二课
- 《More Effective C++》Rule7:千万不要重载 &&, ||, 和 ,操作符
- 关于C++操作符重载之"->"和"*"
- C++程序学习--C++模板类重载左移操作符<<的细节
- 【C/C++】在一个类中重载另一个类的构造函数 ----构造函数是一个特殊的操作符
- C++输入操作符>> 输出操作符<<重载
- C++语法基础--重载operator->箭头操作符
- C++学习笔记:为什么不要重载逻辑与和逻辑或&& || 操作符
- More Effective C++(条款7:千万不要重载&&,||,和,操作符)
- C++中对成员访问操作符->的重载
- 用python实现一个socket echo程序 && tcp socket的几个关闭状态
- C/C++拾遗录--关于一个C语言小程序的分析