《C++大学教程》 第10章 运算符重载:string类 笔记(下)

0. 前言

《C++大学教程》 第10章 后续内容 笔记更一下。附部分课后题代码。

10.11 运算符作为成员函数和非成员函数的比较

当运算符函数作为成员函数实现时,最左边(或者只有最左边)的操作数必须时包含运算符类的一个类对象(或者是对该类对象的一个引用)。

如果左操作数必须是一个不同类的对象或者是一个基本类型对象,那么该运算符函数必须作为非成员函数来实现。
如果非成员运算符函数必须直接访问类的privateprotected成员,那么该函数可以指定为该类的友元函数。

可交换的运算符

非成员函数来重载运算符的另一个原因:使运算符具有可交换性。即类的对象不必非得出现在运算符的左侧

10.12 类型转换

程序员可以使用cast强制类型转换运算符在基本类型之间进行强制转换。

编译器预先并不知道在用户自定义的类型之间、用户自定义类型和基本类型之间如何进行转换,因此程序员必须详细说明该怎么做。
这样的转换可以用构造函数实现,它们是一种将其他类型(包括基本类型)的对象转换成特定类的对象的单参数构造函数

转换运算符

转换运算符也称为强制类型转换运算符,可用于将某一类的对象转换成另一个类的对象。这种转换运算符必须是非static成员函数

下列函数原型

	MyClass::operator char *() const;

声明了一个重载的强制类型转换运算符函数,可以把用户自定义类型MyClass的对象转换成一个临时的char *对象。这个运算符函数声明为const,因为它并不修改原始的对象。重载的强制类型转换运算符函数不指定返回类型,因为返回类型就是对象正要转换成的目标类型

如果s是某个类的对象,当编译器遇到表达式static_cast<char *>(s)时,它会产生函数调用:

	s.operator char *()

把操作数转换成char *

重载强制类型转换运算符函数

	MyClass::operator int() const;
	MyClass::operator OtherClass() const;

声明了两个重载的强制类型转换运算符函数,分别将用户自定义类型MyClass的对象转换成整数和用户自定义类型OtherClass的对象。

强制类型转换运算符和转换构造函数的隐式调用

强制类型转换运算符和转换构造函数的优点之一就是:必要时,编译器可以隐式地调用这些函数来创建临时的对象。

例如,如果用户自定义地String类的对象s出现在程序中一个本该出现普通的char *数据的位置上,如下所示:

	cout << s;

那么编译器便可以调用重载的强制类型转换运算符函数operator char *将对象s转换成char *,并在表达式中使用这个转换结果char *
如果为我们的String类提供这个强制类型转换运算符,那么就不必重载流插入运算符便可以使用cout输出String对象了。

当隐式转换中使用转换构造函数或者转换运算符时,C++只能应用其中的一个(例如单个用户定义的转换)来尝试满足另一个重载运算符的需要。编译器并不会尝试一系列的用户定义的隐式转换来满足重载运算符的需要。

10.13 explicit构造函数与转换运算符

任何单参数的构造函数都可以被编译器用来执行隐式转换,即构造函数接收的类型会转换成定义了该构造函数的类的对象。
任何单参数且不被声明为explicit的构造函数可以被编译器用来进行隐式转换,除了拷贝构造函数。

程序员不必使用强制类型转换运算符进行这种转换,它是自动进行的。但是,在某些情况下,隐式转换是不受欢迎的,或者说这种转换很可能会导致错误。

无意之中将单参数构造函数用作转换构造函数

为了允许隐式转换,我们移除了Array类中的explicit关键字。

class Array
{
   friend std::ostream &operator<<( std::ostream &, const Array & );
   friend std::istream &operator>>( std::istream &, Array & );

public:
   Array( int = 10 ); // default constructor
   Array( const Array & ); // copy constructor
   ~Array(); // destructor
   ... ... 
}; 

// Fig. 10.12: fig10_12.cpp
// Single-argument constructors and implicit conversions.
#include <iostream>
#include "Array.h"
using namespace std;

void outputArray( const Array & ); // prototype

int main()
{
   Array integers1( 7 ); // 7-element Array
   outputArray( integers1 ); // output Array integers1
   outputArray( 3 ); // convert 3 to an Array and output Array抯 contents
}  // end main

// print Array contents
void outputArray( const Array &arrayToOutput )
{
   cout << "The Array received has " << arrayToOutput.getSize() 
      << " elements. The contents are:\n" << arrayToOutput << endl;
} // end outputArray

第13行调用函数outputArray,以整数值3作为其实参。可是,这个程序并没有提供一个接收int参数的名为outputArray的函数。因此,编译器确定Array类是否提供了能把int转换成Array的转换构造函数

将参数3转换成包含了3个元素的临时Array对象。然后,编译器把这个临时Array对象传递给函数outputArray,输出它的内容。

防止无意之中将单参数构造函数用作转换构造函数

声明每个单参数的构造函数时前面加关键字explicit,其目的是禁止不应该允许的由转换构造函数完成的隐式转换。即声明为explicit的构造函数不能再隐式转换中使用。

使用explicit构造函数创建一个包含3个元素的临时Array对象,并将它传递给outputArray函数:

	outputArray( Array(3) );

10.14 重载函数调用运算符()

重载函数调用运算符十分重要,因为函数能接受任意数量的逗号分隔参数。
例如,在自定义的String类中,你能重载该运算符来选择一个String的子类——两个整型作为参数的运算符能指明开始位置和选择的字串的长度。该operator函数能检查诸如起始位置超界或者负长度这样的错误。

重载的函数调用运算符必须是一个非静态的成员函数而且可以被以下方式定义:

	String String::operator()(size_t index, size_t length) const

假设string1是一个包含字符串"AEIOU"String对象。当编译器遇到表达式string(2,3)时,会生成如下的一个成员函数调用:

	String1.operator()(2,3)

该调用返回"IOU"字符串。

另一个关于函数调用运算符可能的应用是启用备用数组下标符号。可能更应该重载函数调用运算符以启用符号chessBoard(row, column),其中chessBoard是一个修改的二维Array类的对象。

练习题

10.8 Complex类

参考本章图10.3、图10.4、图10.10、图10.11。
注意输入格式为英文下的括号和逗号:(real,imaginary)

// Fig. 10.14: Complex.h
// Complex class definition.
#ifndef COMPLEX_H
#define COMPLEX_H

class Complex
{
	friend std::ostream &operator<<(std::ostream &, const Complex &);
	friend std::istream &operator>>(std::istream &, Complex &);
public:
   explicit Complex( double = 0.0, double = 0.0 ); // constructor
   Complex operator+( const Complex & ) const; // addition
   Complex operator-( const Complex & ) const; // subtraction
   Complex operator*(const Complex &) const;
   bool operator==(const Complex &) const;
   bool operator!=(const Complex &right) const
   {
	   return !(*this == right);
   }
private:
   double real; // real part
   double imaginary; // imaginary part
}; // end class Complex

#endif
// Fig. 10.15: Complex.cpp
// Complex class member-function definitions.
#include <iostream>
#include "Complex.h" // Complex class definition
using namespace std;

// Constructor
Complex::Complex( double realPart, double imaginaryPart ) 
   : real( realPart ),
   imaginary( imaginaryPart ) 
{ 
   // empty body
} // end Complex constructor

// addition operator
Complex Complex::operator+( const Complex &operand2 ) const
{
   return Complex( real + operand2.real, 
      imaginary + operand2.imaginary );
} // end function operator+

// subtraction operator
Complex Complex::operator-( const Complex &operand2 ) const
{
   return Complex( real - operand2.real, 
      imaginary - operand2.imaginary );
} // end function operator-

Complex Complex::operator*(const Complex &operand2) const
{
	return Complex(real * operand2.real - imaginary * operand2.imaginary,
		real * operand2.imaginary + imaginary * operand2.real);
} 

bool Complex::operator==(const Complex &operand2) const
{
	if (this->real == operand2.real && this->imaginary == operand2.imaginary)
		return true;
	else
		return false;
}

ostream &operator<<(ostream &output, const Complex &number)
{
	output << "(" << number.real << "," << number.imaginary << ")";
	return output;
}

istream &operator>>(istream &input, Complex &number)
{
	input.ignore();
	input >> number.real;
	input.ignore();
	input >> number.imaginary;
	input.ignore();
	return input;
}
// Fig. 10.16: fig10_16.cpp
// Complex class test program.
#include <iostream>
#include "Complex.h"
using namespace std;

int main()
{
   Complex x;
   Complex y( 4.3, 8.2 );
   Complex z( 3.3, 1.1 );

   cout << "The default value of x is: ";
   cout << x << endl;
   cout << "Enter x in the form (1,1): ";
   // x.print();
   cin >> x;
   cout << "\nx: ";
   cout << x << endl;

   cout << "y: ";
   cout << y << endl;

   cout << "\nz: ";
   cout << z << endl;

   x = y + z;
   cout << "\n\nx = y + z:" << endl;
   cout << x << endl;
   cout << " = ";
   cout << y << endl;
   cout << " + ";
   cout << z << endl;

   x = y - z;
   cout << "\n\nx = y - z:" << endl;
   cout << x << endl;
   cout << " = ";
   cout << y << endl;
   cout << " - ";
   cout << z << endl;

   x = y * z;
   cout << "\n\nx = y * z:" << endl;
   cout << x << endl;
   cout << " = ";
   cout << y << endl;
   cout << " * ";
   cout << z << endl;

   Complex a(1, 1);
   Complex b(1, 1);
   Complex c(2, 2);
   cout << "a: ";
   cout << a << endl;
   cout << "b: ";
   cout << b << endl;
   cout << "c: ";
   cout << c << endl;
   if (a == b)
   {
	   cout << "\na and b are equal\n" << endl;
   }
   else
	   cout << "a and b are not equal\n" << endl;
   if (a == c)
   {
	   cout << "a and c are equal\n" << endl;
   }
   else
	   cout << "a and c are not equal\n" << endl;
} // end main

代码效果

在这里插入图片描述

结语

10.11 Polynomial类写到一半突然发现自己理解地太简单了,就放弃了。
第10章已全部更完。

重载这一章看的稀里糊涂。
下周看继承。
没有效率的一周。

个人水平有限,有问题欢迎各位大神批评指正!

参考链接

部分课后题代码可见:C++大学教程第五版课后答案(第10、11章)

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值