《c++primer学习笔记(下).pdf》由会员分享,可在线阅读,更多相关《c++primer学习笔记(下).pdf(99页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第 1 5 章重载操作符和用户定义的转换15.1操作符重载重载操作符在类体中被声明,生命方式同普通成员函数一样,只不过它的名字包含关键字。perator,以及紧随其后的一个预定义操作符(该操作符必须来自C+预定义操作符的一个子集)。如 String类中声明的operator+=():Class String Public:构造函数重载集后String(const char*=0);String(const String&);析构函数String();赋值操作符重载集合String&operator=(const String&);String&operator=(const char*);重我
2、的下标操作符Char&operator(int)const;等于操作符的重载集后Bool operator=(const char*)const;Bool operator=(const String&)const;String&operator+=(const String&);String&operator+=(const char*);/.成员访问函数I nt size()return _size;Char*c_str()return _string;Private:I nt _size;Char*_string;/.);include Inline String&String:oper
3、ator+=(const String&rhs)lf(rhs._string)String tmp(*this);创建足够大的存储区,以便包含连接后的string_size+=rhs._size;Delete _string;_string=new char_size+1;Strcpy(_string,tmp._string);Strcpy(_string+tmp._size,rhs._string);)Return*this;)inline String&String:operator+=(const char*s)(/如果s 不是空指针if(s)(String tmp(*this);/创建
4、足够大的存储区/以便包含被连接之后的String_size+=strlen(s);delete String;_string=new char _size+1;/首先把原来的String拷贝到新的存储区中/然后,附加上s 所指的C 风格字符串strcpy(.string,tmp._string);strcpy(_string+tmp._size,s);)return*this;)15.1.1类成员与非成员在 String类定义中,我们声明了两个等于操作符,使用这两个等于操作符用法如下:include String.hI nt main()String flower;lf(flower=Tily
5、)ok,使用 String 类重载操作符 operator=(const char*)/.Else if(tulip=flower)错误,编译错误II.代码中Else if(tulip=flower)会导致编译错误,原因在于:只有左操作数是该类类型的对象时,才会考虑使用作为类成员的重载操作符,而这里左操作数不是类类型,所以编译器试图找到 个内置操作符,它可以有个C 风格字符串的左操作数和一个String类型的右操作数,事实上并不存在这样的操作符,所以编译器为main。中第二次使用等于操作符就会产生一个错误信息。除了声明类成员重载操作符,我们还可以声明非类成员重载操作符,如在名字空间中声明等于操
6、作符:Bool operator=(const String&,const String&);Bool operator=(const String&,const char*);全局重载操作符比成员重载操作符多了一个参数,对于成员操作符,隐式的this指针被用作隐式的第一个参数,如:Flower=lily;被编译器重写为Flower.operator=(/lily,/);也就是在成员重载操作符定义中,我们通过this指针可以引用做操作符flow er,而对于全局重载操作符,代表左操作数的参数必须被显示指定。有了针对String类的全局重载操作符,如下表达式:Flower=Tily”;将调用操作
7、符:Bool operator=(const String&,const char*);tulip=flower;将调用操作符:Bool operator=(const String&,const String&);因为当一个重载操作符是一个名字空间函数时,对于操作符的第一个和第二个参数,即等于操作符的左右两个操作数都会考虑转换。那么一般应该如何决定是把一个操作符声明为类成员还是名字空间成员呢?有如下规则:如果个重载操作符是类成员,那么只有当跟它 起被使用的左操作数是该类的对象时它才会被调用。如果该操作符的左操作数必须是其他的类型,那么重载操作符必须是名字空间成员。C+要求赋值=,下标口,调用
8、()和成员访问箭头。操作符必须被定义为类成员操作符,任何把这些操作符定义为名字空间成员的定义都会被标记为编译时刻错误。例如:/错误:必须是类成员char&operator(String&,int ix);除此之外由类设计者决定把操作符声明为一个类成员还是一个名字空间成员。15.1.2 重载操作符的名字只有在C+预定义操作符集中的操作符才可以被重载。域解析操作符::,成员选择符.,成员对象选择符.*,条件操作符?:不能被重载。重载不能改变预定义的操作符的优先级和语法。操作符预定义的操作数个数必须被保留,如一元操作符重载后仍然是一元的。对于内置类型,四个预定义的操作符(+,-,*和&)既可被用作一
9、元操作符,也可被用作二元操作符。操作符的这两种版本都可以被重载。除了对。perator()外对其他重载操作符提供缺省实参都是非法的。15.1.3 重载操作符的设计当重载操作符的意义不是十分明显时,最好不要提供它。15.2友元回顾上节,在名字空间域中定义的String类等于操作符定义如下:bool operator=(const String&strl,const String&str2)(if(strl.size()!=str2.size()这里使用成员访问函数return false;return strcmp(strl.c_str()z str2.c_str()?false:true;)而
10、如果将等于操作符作为类成员操作符重载函数的话,如下:bool String:operator=(const String&rhs)const(if(_size!=rhs._size)return false;return strcmp(_string,rhs._string)?false:true;)可以看到区别:在名字空间域中定义的等于操作符,不能直接引用String的私有数据成员,它使用访问成员函数size。和 c_str()来获得String对象的大小及底层的c 风格字符串。另外一种实现方式时:把名字空间域的等于操作符声明为String类的友元,通过把函数或操作符声明为友元,一个类可以授
11、予这个函数或操作符访问器非公有成员的权利。友元声明以关键字friend开始,它只能出现在类定义中,因为友元不是授权类的成员,所以它不受其所在类的声明区域(public,private protected)影响。这里我们选择把所有友元声明组织在一起并放在类头之后:class String friend bool operator=(const String&,const String&);friend bool operator=(const char*,const String&);friend bool operator=(const String&,const char*);public:
12、11.String类中的其他部分);String类中的三个友元声明把全局域中声明的三个重载的比较操作符声明为String类的友元。既然这些等于操作符已经被声明为友元,那么它们的定义就可以直接引用String的私有成员了。/friend操作符直接引用String的私有成员/friend operators:refer to String private members directlybool operator=(const String&strl,const String&str2)(if(strl._size!=str2._sjze)return false;return strcmp(st
13、rl._string,str2._string)?false:true;)inline bool operator=(const String&str,const char*s)return strcmp(str._string,s)?false:true;)/以下略在使一个类成为另一个类的友元时,友元类的成员函数被赋予访问授权类的非公有成员的权利。如果一个函数操纵两个不同类类型的对象,而且该函数需要访问这两个类的非公有成员,则这个函数可以被声明为这两个类的友元,或者作为一个类的成员函数,并声明为另一个类的友元:(1)声明为两个类的友元。class Window;/只声明class Scree
14、n friend bool is_equal(Screen&,Window&);II.;class Window friend bool is_equal(Screen&,Window&);n.;(2)作为一个类的成员函数,并声明为另一个类的友元。class Window;class Screen public:1 1 copy是类Screen的成员Screen©(Window&);n.);class Window 1 1 copy是类W indow的一个友元friend Screen&Screen:copy(Window&);n.);只有当一个类的定义已经被看到时,它的成员函数才能被
15、声明为另一个类的友元。这并不总是能够做到的,例如,如 果 Screen类必须把Window类的成员函数声明为友元,而Window类必须把Screen类的成员函数声明为友元,该怎么办呢?在这种情况下可以把整个 Window类声明为Screen类的友元。例如class Window;class Screen friend class Window;II.);Screen类的非公有成员现在可以被Window的每个成员函数访问。15.3操作符=回顾前面关于String类的定义:class String public:/char*的赋值操作符String&operator=(const char*);/
16、.private:int _size;char*_string;);String&String:operator=(const char*sobj)(/so b j是个空指针if(!sob j)_size=0;delete _string;_string=0;else _size=strlen(sobj);delete _string;.string=new char _size+1;strcpy(_string,sobj);)return*this;)因为有了上面的定义,下面语句才正确:String car(,volks/,);Car=Studebaker;这里要注意的是:等于操作符的参数是
17、const指针,这是因为如果定义为非const指针,则禁止了下面这样的赋值表达式:Car=Studebaker;因为c 风格的字符串的类型是const char*15.4 操作符口我们可以为“表示容器抽象并能够获取其单独元素的类”定义下标操作符。perator口()。15.5 操作符 operator()如果一个类类型被定义来表示一个操作时,则可以为这个类类型重载函数调用操作符,以便调用这个操作。例如,abslnt类被定义为将取一个int型操作数的绝对值的操作封装起来:class abslnt public:int operator()(int val)int result=val 0?-va
18、l:val;return result;);重载的operator。必须被声明为成员函数,它的参数表可以有任意数目的参数。下面展示了如何使用abslnt类的重载operator。:#include ftinclude int main()int ia=-0,1,-1,-2,3,5,-5,8;vector ivec(ia,ia+8);/把 ivec的每个元素设置为其绝对值transformf ivec.begin(),ivec.end(),ivec.begin(),abslnt();II.Transform。的第四个实参是一个abslnt类型的临时对象,它通过调用anslnt的缺省构造函数来创建
19、,产生 个函数对象,该函数对象被用来调用abslnt类的重载的。perator()。15.6操作符,我们也可以为类类型的对象重载成员访问操作符箭头,它必须被定义为一个类的成员函数。它的作用是赋予一个类类型与指针类似的行为。它通常被用于一个代表智能指针(smartpointer)的类类型。也就是说,一个类的行为很像内置的指针类型,但是支持某些额外的功能。假设,我们想定义一个类类型来代表一个指向Screen类对象(Screen类在地13章介绍)的指针,定义如下:class ScreenPtr public:ScreenPtr(Screen&s):ptr(&s)n.private:Screen*pt
20、r;);ScreenPtr类型的对象的定义必须提供初始值:-个Screen类型的对象,ScreenPtr对象将指向它,否则ScreenPtr对象的定义就是错误的。ScreenPtr pl;/错误:ScreenPtr没有缺省构造函数Screen myScreen(4,4);ScreenPtr ps(myScreen);/ok为使ScreenPtr类的行为像内置指针,我们必须再定义一些重载操作符,我们定义的两个操作符是:解引用操作符*和成员操作符箭头-:/支持指针行为的重载操作符class ScreenPtr public:Screen&operator*()return*ptr;Screen*o
21、perator-()return ptr;/A.);重载的成员访问操作符箭头的返回类型必须是一个类类型的指针,或 者 是“定义该成员访问操作符箭头的类”的一个对象,如果返回类型是一个类类型的指针,则内置成员访问操作符箭头的语义被应用在返回值上。如果返回值是另外一个类的对象或引用,则递归应用该过程,直到返回的是指针类型或语句错误。例如我们可以用ScreenPtr对 象 p s访问Screen类的成员,如下所示:ps-move(2,3);因为成员访问操作符箭头的左操作数的类型是ScreenPtr,所以使用该类的重载操作符。该操作符返回个指向Screen类对象的指针,内置成员访问操作符箭头被依次应用
22、在这个返回值上,以调用Screen类的成员函数move。下面的小程序使用了我们的ScreenPtr类,ScreenPtr类型的对象用起来就像Screen*类型的对象一样:include/include include Screen.hvoid printScreen(ScreenPtr&ps)(cout Screen Object(ps-height(),H ps-width()nn;for(int ix=1;ix height();+ix)(for(int iy=1;iy width();+iy)cout ps-get(ix,iy);coutn;)int main()Screen sobj(
23、2,5);string iint(HelloWorld);ScreenPtr ps(sobj);/设置屏幕的内容string:size_type iintpos=0;for(int ix=1;ix height();+ix)for(int iy=1;iy width();+iy)(ps-move(ix,iy);ps-set(iint iintpos+);)/输出屏幕的内容printScreen(ps);return 0;)15.7 操作符+和一为了区分后置递增/递减操作符和前置递增/递减操作符的声明,重载的递增/递减操作符的声明有一个额外的int类型的参数。15.8 操作符 new 和 del
24、ete缺省情况下,空闲存储区中的类对象的分配和和释放,由在C+标准库中定义的全局操作 符 new()和 delete()来执行。但是如果一个类提供了两个分别被称为new O 和 deleteO的成员函数,那么他们可以承接自己的内存管理权。下面是Screen类操作符new()的声明:Class Screen Public:Void operator new(size_t);new()的返回类型必须是 void*,参数必须是 size_t类型,size_t定义在系统头文件中void operator delete(void*);类成员操作符delete O的返回类型必须是vo id,且第一个参数的
25、类型是void*。II.)当 new 表达式创建一个类类型的对象时,编译器查看该类是否有一个成员操作符new(),如果有则选择这个操作符为该类对象分配内存,否则,调用全局操作符new()o操作符的size_t参数自动被初始化为Screen类的字节大小。程序员可以使用全局域解析操作符来选择调用全局操作符new()。例如:Screen*ps=:new Screen;调用了全局操作符new(),即使Screen类也定义了 new()操作符作为其成员。当 delete表达式的操作数是指向一个类类型对象的指针时,编译器检查该类是否有一个成员操作符delete。如果有,则选择该操作符为类对象释放内存,否则
26、调用全局操作符delete()o程序员可以通过使用全局域解析操作符有选择地调用全局操作符delete。例如::delete ps;调用全局域中定义的操作符delete。,即使Screen类把操作符delete。定 义 为 它 的 个成员。为一个类类型定义的delete()操作符,可以有两个参数:第一个参数必须是void*,第二个参数必须是size。例如:class Screen public:replaces:/void operator delete(void*);void operator delete(void*,size_t););对 于 delete。,如果存在第二个参数,则它将被编
27、译器用第一个参数所指对象的字节大小自动初始化。一般来说被使用的操作符delete。应该与用来分配存贮区的new()操作符相匹配。但是我们可以只为一个类声明new()而不声明delete(),或者只声明delete()而不声明new()o操 作 符 new()和 delete。都是类的静态成员(其原因是这些操作符被调用的时候,要么是在该类对象被创建之前 操作符new。,要么是在其被销毁之后 操作符delete。,它们遵从静态成员函数的一般限制。这些操作符被自动做成静态成员函数,而无需程序员显式地把它们声明为静态的。尤其要记住的是,静态成员函数没有this指针,因此它们只能访问静态数据成员。用操作
28、符new。的分配动作,如:Screen*ptr=new Screen(10,20);与下列双语句序列等:/C+伪码ptr=Screen:operator new(sizeof(Screen);Screen:Screen(ptr,10,20);即 new表达式先调用该类的操作符new()来分配存贮区,然后再调用构造函数初始化该对象,如果操作符new()失败,则抛出bad_alloc类型的异常,并且不会调用构造函数。用操作符delete。释放存贮区的动作,如:delete ptr;与下列双语句序列等价 C+伪玛Screen:Screen(ptr);Screen-operator delete(pt
29、r,sizeof(*ptr);即 delete表达式首先调用该对象的析构函数,然后再调用该类的操作符delete。释放存贮区,如果ptr的值是0,则不会调用析构函数和操作符delete。15.8.1数组操作符new口和delete口/如果screen类定义了 new操作符,则调用screen:operator new(),否则调用全局new操作符Screen*ps=new Screen(24,88);/调用 operator new()Screen*psa=new Screen10;我们也可以把针对数组分配的操作符new口()和 delete口()声明为类的成员。卜面是Screen类操作符ne
30、w()和 delete口()的声明:Class Screen public:void*operator new(size_t);void operator delete(void*);II.类成员操作符new口()的返回类型必须是void*,并且第一个参数的类型是size。当一个new 表达式创建个类类型对象的数组时,编译器将检查该类是否有成员操作符 new口(),如果有则用该操作符来分配数组的内存,否则将调用全局操作符new口()。下面的new表达式在空闲存贮区中创建了一个包含10个 Screen类对象的数组:Screen*ps=new Screen10;操作符的size_t参数被自动初始化
31、,其值等于存放10个 Screen对象的数组所需内存的字节大小。即使Screen类有一个成员操作符new口(),程序员也可以通过全局域解析操作符,来调用全局new()来创建Screen对象的数组。例如下面的new表达式调用了全局域中定义的操作符 new():Screen*ps=:new Screen10;成员操作符delete口()的返回类型必须是v o id,它的第一个参数必须是void*类型。为删除个类的数组,delete表达式必须使用数组语法:delete口 ps;当这样的delete表达式的操作数是一个指向类类型的指针时,编译器就会检查该类是否有成员操作符delete口(),如果有则用
32、该操作符来释放数组的内存,否则调用全局操作符delete口()。操作符的void*参数被自动初始化,其值等于数组存储区的起始处。即使Screen类 有个成员操作符delete(),程序员也可以通过全局域解析操作符有选择地调用全局操作符delete口()。例如::delete ps;调用了全局域中定义的操作符delete。.创建数组的new表达式首先调用类操作符new()来分配存贮区,然后再调用缺省构造函数依次初始化数组的每一个元素,如果这类定义了构造函数,但是没有缺省构造函数,则相应的new表达式就是错误的,因为没有任何C+语法可以为数组元素指定初始值或者在数组版本的new表达式中为类的构造函
33、数指定实参。删除数组的delete表达式先调用类的析构函数依次销毁数组的每一个元素,然后再调用类操作符delete口()来释放内存。一个类的操作符delete口()也可以有两个参数:第一个参数依然必须是vo id*,第二个参数的类型是预定义类型size。例如:class Screen public:代替:void operator delete)void*);void operator delete(void*,size_t);;如果存在额外的参数,则它由编译器自动初始化,其值等于存贮数组所需内存的字节大小。15.8.2 定位操作符 new()和 delete()我们亦可以重载类的成员操作符n
34、ew(),只要每个声明都有唯一的参数表。但是任何一个类操作符new()的第一个参数的类型都必须是size。例如:class Screen public:void*operator new(size_t);void*operator new(size_t,Screen*);n.;额外的参数可以被new表达式中指定的定位实参初始化。例如:void func(Screen*start)Screen*ps=new(start)Screen;/placement newII.在 new表达式中的关键字new后面、出现在括号中的部分表示定位实参。上面的new表达式调用双参数的成员操作符n e w(),第一
35、个参数被自动初始化为Screen类的字节大小值,第二个参数被初始化为定位实参start的值。我们也可以重载类成员操作符delete。,但是,这样的操作符不会以delete表达式的方式被调用。如果new表达式调用的构造函数抛出一个异常的话,重载的操作符delete。只能被编译器隐式地调用。考虑下面的表达式:Screen*ps=new(start)Screen;该表达式包含如下动作:1 调用类 Screen 的操作符 new(size_t,Screen*)2 接着,调用类Screen的缺省构造函数初始化该对象3 然后,用 Screen对象的地址初始化ps假 设 在 new(size_t,Scree
36、n*)中会调用全局操作符new()分配内存,在第二步调用的Screen构造函数时抛出一个异常,那么类的设计者需要确保分配的内存被正确释放。这是通过提供重载的操作符delete。实现的 这也是重载delete。的唯一用处。如果前面情形发生,则编译器在Screen类的域中查找一个操作符delete。但是这个delete。比较特殊:它必须具有与被调用new()操作符的参数类型相匹配的参数,因为操作符new()的第个参数总是size_t,delete。的第一个参数总是vo id*,所以我们这里所说的相匹配是指第二个参数匹配,也就是要找下面形式的操作符deleteO:void operator dele
37、te(void*,Screen*);如果new表达式调用的构造函数抛出一个异常,并且在Screen类中找到了这样的delete()操作符,则编译器就调用它来释放内存,如果没有找到这样的操作符delete。,则不会调用任何delete。操作符。根据操作符new()是否分配内存或者是否重新使用已分配的内存,类设计者可以决定是否提供与特定操作符new()相匹配的操作符delete。如果说操作符new()分配了内存,则应该提供定位操作符delete。,以便当“new表达式调用的构造函数抛出异常”时可以正确地释放内存。如果定位操作符new。没有分配内存,则无需提供相匹配的操作符delete。来释放内存。
38、我们也可以重载针对数组的定位操作符new()和 delete口():class Screen public:void*operator new(size_t);void*operator new(size_t,Screen*);void operator delete(void*,size_t);void operator delete(void*,Screen*);n.);当分配数组的new表达式指定匹配的定位实参时,定位操作符new口()就会被使用。例如:void func(Screen*start)调用 Screen-operator new(size_t,Screen*)Screen*
39、ps=new(start)Screen10;II.)如果由该new表达式调用的构造函数抛出一个异常,则编译器就会自动调用在Screen类中定义的、匹配的重载操作符deletee口()。15.9用户定义的转换回 顾 个经典的类型转换:Char ch;Int ival;Ch+ival;/ch被提升为int型,提升动作由编译器隐式处理在本节中,我们考虑的是类的设计者怎样为类类型的对象提供一组用户定义的转换,这些用户定义的转换也是由编译器在需要时隐式的调用的。C+提供了一种机制,通过它,每个类都可以定义一组可被应用在该类型对象上的转换。刻于Smalllnt我们定义了一个从Sma川 nt对象到int型的
40、转换,下面是实现:class Smalllnt public:Smalllnt(int ival):value(ival)/转换操作符/Smalllnt=intoperator int()return value;/没有提供近载操作符private:int value;);操作符int()是个转换函数,它定义了一个用户定义的转换。用户定义的转换,是在类类型和转换函数中指定的类型之间的转换。现 在 Smalllnt类对象可以被用在任何可以使用int对象的地方,假设不再提供重载的操作符并且类Smalllnt提供了一个向int型转换的函数,下列加法:Smalllnt si(3);si+3.14159
41、;被解析为如下两步:1 调用Smalllnt转换函数产生整型值32 整型值3 被提升为3.0 并与double文字常量3.14159相加生成double型 6.14159下面我们修改smalllnt类,提供额外的支持:#include class Smalllnt friend istream&operator(istream&is,Smalllnt&s);friend ostream&operator(ostream&os,const Smalllnt&s)return os s.value;public:Smalllnt(int i=0):value(rangeCheck(i)int op
42、erator=(int i)return(value=rangeCheck(i);operator int()return value;private:int rangeCheck(int);int value;);在类体之外被定义的成员函数的定义如下istream&operator(istream&is,Smalllnt&si)int ix;is ix;si=ix;/Smalllnt:operator=(int)return is;)int Smalllnt:rangeCheck(int i)(/*如果前8 位以外的位被置位*则报告值太大了:然后退出*/if(i&-0377)cerr en*
43、Smalllnt range error:i *endl;exit(-1);return i;)1 5.9.1 转换函数转换函数是一种特殊类型的类成员函数,它定义了一个由用户定义的转换,以便把一个类对象转换成某种其他的类型。在类体中通过指定关键字operator,并在其后加上转换的目标类型后,我们就可以声明转换函数。在转换函数的声明中,关键字operator后面的名字不一定必须是内置类型的名字,如下:#include Smalllnt.htypedef char*tName;class Token public:Token(char*,int);operator Smalllnt()retur
44、n val;operator tName()return name;operator int()return val;/其他公有成员private:Smalllnt val;char*name;);void print(int i)cout print(int):i endl;)Token tl(integer constant,127);Token t2(friend,255);int main()(编译器隐式的把token型实参t l 和 t2 转换成int型print(t l);/tl.operator int()print(t2);/t2.operator int()return 0;
45、编译并运行这个小程序,生成下列输出:print(int):127print(int):255转换函数采用如下的般形式operator type();这里的type可用内置类型、类类型或typedef名取代,但是不允许type表示数组或函数类型,转换函数必须是成员函数,它的声明不能指定返回类型和参数表,例如下列声明都是错误的:operator int(Smalllnt&);/错误:不是成员class Smalllnt public:int operator int();/错误:返回类型operator int(int=0);/错误:参数表/.;显式的强制类型转换会导致调用转换函数。如果被转换值的
46、类型是一个类类型,它有个转换函数,并且该转换函数的类型是强制转换所指定的类型,则调用这个类的转换函数。例如:include Token.hToken tok(function,78);/函数型的表示法:调用Token:operator Smalllnt。Smalllnt to kVa I=Smalllnt(tok);/static_cast:调用 Token:operator tName()char*tokName=static_cast(tok);Token:operator tName()等价于 Token:operator char*。,这样可能会有副作用:#include Token.
47、hToken tok(function,78);char*tokName=tok;/ok 隐式转换 Token:operator tName()*tokName=P;/喔!Token 的 name 成员现在是叩unction”!我们的意图是,只允许对被转换的Token类对象进行只读访问。为实现这个目的,转换操作符必须返回一个const char*:typedef const char*cchar;class Token public:operator cchar()return name;/.;/错误:不允许把char*转换成const char*char*pn=tok;const char*
48、pn2=tok;/ok使用转换函数时,转换的目标类型不必一定要与转换函数的类型完全匹配:只要转换的目标与转换的类型满足,目标类型可以通过标准转换序列(间 9.3节)到 达。如:extern void calc(double);Token tok(constant,44);/调用 tok.operatofnt()吗?是的/int-double通过标准转换calc(tok);在用户定义的转换之后,只允许标准转换序列,如果为了到达目标类型必须应用第二个用户定义的转换,则编译器不会隐式应用任何转换。例如,如果Token没有定义operator in t(),则 F 列调用是非法的:extern voi
49、d calc(int);Token tok(pointer,37);没 有定义Token:operator ln t(),这个调用会产生编译时刻错误,因为tok至!int型的转换需要经历:Token:operator Smalllnt()+Smalllnt:operator int()calc(tok);15.9.2用构造函数作为转换函数(略)15.10 选择一个转换15.11 重载解析和成员函数成员函数也可以被重载。对于一个成员函数的调用,编译器也使用函数重载解析过程来选择最佳可行函数。成员函数的重载解析与非成员函数的重载解析十分类似。该过程由下列相同的三步骤组成:1 选择候选函数2 选择可
50、行函数3 选择最佳匹配函数为成员函数调用选择候选函数和可行函数,其做法与以前有一点小小的不同,我们将在本节介绍这些区别。15.11.1 重载成员函数声明类的成员函数也可以被重载。例如:class myClass public:void f(double);char f(char,char);/重载 myClass:f(double)/.;如同在名字空间域中声明的函数样,在类中声明的成员函数可以有相同的名字,只要参数表惟一:或者在参数的数目上不同,或者在参数的类型上不同,如果以同一名字声明的两个成员数只有返回类型不同,则第二个声明被视为错误的声明,被标记为编译时刻错误。例如:class myCl