《C++ Primer Plus》读书笔记之九—使用类
第十一章 使用类
1、操作符函数的格式:operator op(argument-list)。op是将要重载的操作符。
2、操作符重载函数的两种调用方式:①函数表示法:C=A.operator+(B);A、B、C都是类对;②操作符表示法:C=A+ B;操作符左侧的对象A是调用对象,操作符右边的对象B是作为参数被传递的对象。
3、不要返回指向局部变量或临时对象的引用,函数执行完毕后,局部变量和临时对象将消失,引用将指向不存在的数据。
4、C++对用户定义的操作符重载的限制:①重载的操作符不必是成员函数,但必须至少有一个操作数是用户定义的类型。这将防止用户为标准类型重载操作符。②使用操作符时不能违反操作符原来的句法规则,不能修改操作符的优先级。③不能定义新的操作符。④不能重载下面的操作符:(sizeof)、(.)、(.*)、(::)、(?:)、typeid、const_cast、dynamic_cast、reiniterpret_cast、static_cast。⑤下面的操作符只能通过成员函数进行重载:(=赋值操作符)、(()函数调用操作符)、([]下标操作符)、(->)。
5、注意:重载的成员函数操作符,左侧的操作数是调用对象。
6、非成员函数,不是由对象调用的,它使用的所有值(包括类对象)都是显式参数。但是,如果函数对类对象有所操作,非成员函数不能直接访问类的私有成员。这时候,需要一类特殊的函数——友元函数。
7、友元函数:将原型放在类声明中,并在原型声明前加上关键字friend:①友元函数不是成员函数,因此不能使用成员操作符来调用。②虽然友元函数不是成员函数,但它与成员函数的访问权限相同。注意:编写友元函数定义时,不要使用类作用域限定符(classname::),不要在定义中使用关键字friend。
8、如果要为类重载操作符,并将非类的项作为第一个操作数,则可以用友元函数来反转操作数的顺序,因为第一个操作数必须为类对象。
9、cout是一个ostream对象,它是智能的,能够识别所有的C++基本类型。这是因为对于每种基本类型,ostream类声明中都包含了相应的重载的operator<<()定义。
10、如果想要用cout输出一个类对象的值,可以通过在类中声明一个友元函数来重载operator<<()函数,如下:
ostream & operator<< (ostream & os,const Time & t)
{
os<<t.hours<<t.minutes;
return os;
}
那么,Time trip;cout<<trip;被转化成operator<<(cout,trip);
11、对于很多操作符来说,可以选择使用成员函数或非成员函数来实现操作符重载。一般来说,非成员函数应是友元函数,这样它才能直接访问私有数据。
12、记住:非成员版本的重载操作符函数所需的形参数目与操作符使用的操作数目相同;而成员版本所需的参数数目少一个,因为其中的一个操作数是通过this指针被隐式的传递的调用对象。
13、对于两个Time类对象T2、T3,求T1=T2+T3;可以用两种格式实现,如下。但要注意:不能同时选择这两种格式,因为这两种格式都与同一个表达式匹配,同时定义这两种格式将被视为二义性错误,导致编译错误。
T1=T2.operator+(T3); // 成员函数
T1=operator+(T2,T3); // 非成员函数(友元)
14、因为操作符是通过函数是实现的,所以只要操作符函数的特征标相同,使用的操作符数量与相应的内置C++操作符相同,就可以多次重载同一个操作符。
15、将一个标准类型变量的值赋给另一种标准类型的变量时,如果这两种类型兼容,则C++自动将这个值转换为接收变量的类型。 C++语言不自动转换不兼容的类型(例如:int *p=10;非法,不过可以强制类型转换:int *p=(int*)10;)。
16、C++允许指定在类和基本类型之间进行转换的方式。首先,任何接受唯一一个参数的构造函数都可被用作转换函数,将类型与该参数相同的值转换为类。如果将类型与该参数相同的值赋给对象,则C++将自动调用该构造函数。例如:有类Stonewt,则Stonewt mycat;mycat=19.6;程序将使用构造函数Stonewt(double)来创建一个临时的Stonewt对象,并将19.6作为初始值。随后,采用逐成员赋值方式将该临时对象的内容复制到mycat中。这一过程称为隐式转换。不过,如果该构造函数的声明前加上了关键字explicit,则该构造函数不能用于这种隐式转换,只能用于显式转换:mycat=Stonewt(19.6);
17、何时使用Stonewt(double)进行隐式转换呢?
①将Stonewt对象初始化为double值时。
②将double值赋给Stonewt对象时。
③将double值传递给接受Stonewt参数时。
④返回值被声明为Stonewt的函数试图返回一个double值时。
⑤在上述任一种情况下,使用可转换为double类型的内置类型时(先将内置类型转换成double,再调用构造函数)。
18、Stonewt mycat=19.6等价于:①Stonewt mycat(19.6);②Stonewt mycat=Stonewt(19.6);不过后两种格式可用于接受多个参数的构造函数。
19、构造函数只用于从某种类型到类类型的转化,要进行相反的转换(即将类转换成某种类型),必须使用特殊的C++操作符函数——转换函数。转换函数是用户定义的强制类型转换。
20、对于转换函数,注意以下几点:①转换函数必须是类方法。②转换函数不能指定返回类型。③转换函数不能有参数。具体的格式为:
operator typename();例如转换为double类型的函数原型为:operator double();注:转换函数是类方法意味着:它需要通过类对象来调用,从而告知函数要转换的值。因此,函数不需要参数。
21、转换函数的隐式转换:double mycat1=mycat;如果只定义了double转换函数,因此不存在二义性,但是如果定义了int和double两个转换函数operator double();和operator int();则该语句long mycat2=mycat;有二义性而被拒绝!因为int和double值都能赋给long值,所以编译器认为使用任意一个转换函数都是合法的。但是如果删除了其中一个,编译器将接受该语句。
22、当类定义了两种或多种的转换时,仍可以用显式强制类型转换来指出使用哪个转换函数:long mycat2=(double)mycat;或者long mycat2=int(mycat);
23、实现加法时的选择:比如要将double量(或者其他内置类型)和Stonewt (类对象)量相加,有两种选择:
①将operator + (const Stonewt&,const Stonewt&)定义为友元函数,让Stonewt(double)构造函数将double类型的参数转换为Stonewt类型的参数。
②将加法操作符重载为一个显式使用double类型参数的函数:Ⅰ、Stonewt operator +(double X);//成员函数 Ⅱ 、friend Stonewt operator+(double X,Stonewt & s); // 友元函数
优缺点:第一种方法依赖于隐式转换,使程序更简短,因为定义的函数较少。缺点是每次转换时,都将调用转换构造函数,增加时间和内存开销。第二种方法(增加一个显式的匹配类型的函数)则刚好相反。它使程序更长,但运行速度较快。