C++ Primer 第十四章操作重载与类型转换 14.1~14.7 练习和总结

第14章的内容过一遍就可以了,通过重载运算符实现的方法,定义函数也可以实现。

重载运算符类型 建议定义在哪里 返回值类型 参数类型 注意事项
<< 非成员 ostram& ostream&,const CLS&
>> 非成员 istream& istream&,CLS& 需要考虑输入流错误的情况,一般将对象置为空
算术运算符 非成员 CLS const CLS&,const CLS& 定义了+,一般就需要定义+=,定义了-,一般就需要定义-=
关系运算符 非成员 bool const CLS&,const CLS& 通常定义了==就需要定义!=,定义了<就需要定义>,<=,>=
赋值,复合赋值运算符 成员 CLS& const CLS&
下标运算符 成员 CLS& size_t 一般需要定义一个const和一个非const版本
前置递增递减 成员 CLS& -
后置递增递减 成员 CLS int int形参只用来区别前置和后置,一啊不能不会用到这个参数
解引用* 成员 CLS& -
箭头访问符 成员 CLS* - 箭头访问符一定要访问一个类的成员,如p->size()否则报错

14.1 基本概念

我们可以重载大部分的运算符,重载运算符可以提高代码的简洁程度。

重载运算符需要operator+运算符的名字来重载运算符。

下面是可以重载的运算符。
在这里插入图片描述
调用运算符其实是调用函数,所以我们可以重载的运算符,一样的需要返回值,形参列表,函数体。但是不能类型的运算符返回值和参数个数是不一样的

bool operator+(A&,A&){
	
}
A& A::operator+=(A&){}
{
	
}

因为重载运算符实际上就是一个函数,拿在外部重载的+作为例子,我们可以这样做,只不过这样做不好看。

a+b;
operator+(a,b);

重载之后的运算符优先级还是和内置的一样。

重载的运算符可以作为成员函数也可以作为普通函数,如果是成员函数则运算符的第一个或者左侧的运算符对象将绑定到*this上。所以写在类中的重载运算符将会少一个参数。

具体什么时候定义在类中,什么时候定义在外部,则看具体的使用情况。

书上是这样说的:
在这里插入图片描述
可以看到有一些必须定义在类中,有一些则类内,类外都可以。
还有一些对称性的运算符,则需要定义在外部。

练习

14.1

如果重载的运算符为成员函数,那么运算符左侧或者第一个运算对象将绑定到*this上。对于算数,关系,==等对称性运算符,其左侧(第一个)运算对象必须为类类型。

如果重载的运算符不是成员函数,则操作规则和内置类型一样。

14.2
加法
Sales_data operator+(Sales_data& data1, Sales_data& data2) {
	if (data1.isbn() == data2.isbn())
	{
		data1.combine(data2);
	}
	return data1;
}

复合赋值
inline Sales_data & Sales_data::operator+=(Sales_data & data)
{
	// TODO: 在此处插入 return 语句
	if (data.isbn()==isbn())
	{
		combine(data);
	}
	return *this;
}
14.3

这里我认为定义了重载的==,是在成员函数内部重载的。

a。调用系统内置的==
b。调用string的
c。调用vector的
d。调用string的

14.4

a。左右对象都可以是运算符对象,所以不建议定义为成员函数
b。应该
c。应该
d。应该
e。从后面的内容得知不应该
f。&&不应该
g,不应该
h,应该

14.5

我写的是Date。
一般不用重载运算符,因为如果定义+,2010/1/22+2010/2/22等于多少呢?没有统一的标准,所以重载运算符,而是提供add_month(),add_day()这样的接口更好。

当然我们可以定义两个Date相减得到一个日期。

这里就不具体实现了。

14.2 输入和输出运算符

重载输入和输出运算符定义为非成员函数。
通常重载的输入运算符需要处理输入流错误(异常)的情况,通常的做法是将对象的状态置为空。

练习

14.6,14.7,14.8 形式都是一样的,不再重复

ostream& operator<<(ostream& os,const Sales_data& data) {
	os << data.bookNo << " " << data.units_sold << " " << data.revenue;
	return os;
}
14.9
istream&  operator>>(istream& is, Sales_data & data)
{
	double price;
	is >> data.bookNo >> data.units_sold >> price;
	//is >> data.units_sold;
	if (is)
	{
		data.revenue = data.units_sold*price;
	}
	else {
		data = Sales_data();
	}
	return is;
	// TODO: 在此处插入 return 语句
}
14.10

a.得到一个正确的对象
b。因为price为double类型,而输入的是字符串,所以流的状态变为异常,所以得到一个空对象

14.11

存在,这个函数没有处理输入流错误时的情况

发生未定义了的行为,此时bookNo和units_sold都已经被赋值,但是price的值将是默认的值(如果设有默认值的话)

14.12
istream & operator>>(istream & is, My_Date & date)
{
	is >> date.year >> date.month >> date.day;
	if (!is)
	{
		date = My_Date();
	}
	return is;
	// TODO: 在此处插入 return 语句
}

14.3 算术和关系运算符

练习

14.13

可以支持==运算符

略,这些操作做一个熟悉一下就可以了

14.14

因为+=返回的是引用不用创建对象

14.15

两个日期并不好做算术运算,可以相减得到一个具体天数,但是其返回值类型并不是MyDate,这样就违背了尽量不改变运算符原本意义的初衷。

14.16

14.17
bool operator==(const My_Date & d1, const My_Date & d2)
{
	return d1.year==d2.year&&d1.month==d2.month&&d1.day==d2.day;
}

bool operator!=(const My_Date & d1, const My_Date & d2)
{
	return !(d1==d2);
}
14.18

14.19

定义了小于和大于,注意,在判断大于时,需要判断是否小于等于


bool operator<(const My_Date & d1, const My_Date & d2)
{
	bool flag = true;
	if (d1.year>d2.year)
	{
		flag = false;
	}
	else if (d1.year==d2.year&&d1.month>d2.month) {
		flag = false;
	}
	else if (d1.year == d2.year&&d1.month==d2.month&&d1.day>d2.day) {
		flag = false;
	}
	return flag;
}

bool operator>(const My_Date & d1, const My_Date & d2)
{
	return (d1!=d2)&&!(d1<d2);
}

14.4 赋值运算符

练习

14.20

14.21

+=中调用+,因为+返回一个非引用类型的对象就意味着需要构建一个临时对象。

14.22,14.23

14.24

因为My_Date中不需要管理其他的资源,没有指针类型。所以移动赋值对其作用不大。

	My_Date(const My_Date& d) :year(d.year), month(d.month), day(d.day) {};

14.25

实现复合赋值的意义不大,这里的运算对象左值右值类型都可以。
因为const的引用既可以绑定到左值上也可以绑定到右值上

My_Date & My_Date::operator=(const My_Date & d)
{
	year = d.year;
	month = d.month;
	day = d.day;
	// TODO: 在此处插入 return 语句
	return *this;
}
14.26

string & StrBlobPtr::operator[](size_t index)
{
	// TODO: 在此处插入 return 语句
	auto p =check(curr+index, "index out of range");
	return (*p)[curr+index];
}

string & StrVec::operator[](const size_t index)
{
	// TODO: 在此处插入 return 语句
	return *(elements + index);
}

string & StrVec::operator[](const size_t index) const
{
	// TODO: 在此处插入 return 语句
	return *(elements + index);
}

14.6 递增递减运算符

练习

14.27,14.28
StrBlobPtr & StrBlobPtr::operator++()
{
	check(curr, "index out of rage");
	++curr;
	return *this;
	// TODO: 在此处插入 return 语句
}

StrBlobPtr & StrBlobPtr::operator--()
{
	// TODO: 在此处插入 return 语句
	--curr;
	check(curr, "index out of rage");
	return *this;
}

StrBlobPtr  StrBlobPtr::operator++(int)
{
	auto temp = *this;
	++*this;
	return temp;
	// TODO: 在此处插入 return 语句
}

StrBlobPtr StrBlobPtr::operator--(int)
{
	auto temp = *this;
	--*this;
	return temp;
	// TODO: 在此处插入 return 语句
}

StrBlobPtr StrBlobPtr::operator+(size_t v)
{
	//检查加上一个值之后是否越界
	check(curr + v, "add size cause index out of range");
	StrBlobPtr ptr = *this;
	ptr.curr = curr + v;
	return ptr;
}

StrBlobPtr StrBlobPtr::operator-(size_t v)
{
	check(curr - v, "sub size cause index out of range");
	StrBlobPtr ptr = *this;
	ptr.curr = curr - v;
	return ptr;
}
14.29

因为递增递减需要改变对象本身,定义为const版本就不能改变自身了。

14.7 成员访问运算符

练习

14.30
string & StrBlobPtr::operator*()const
{
	// TODO: 在此处插入 return 语句
	auto ret =  check(curr, "point out of range");
	return (*ret)[curr];
}
//箭头元素运算符,必须访问一个成员
string * StrBlobPtr::operator->()const
{
	auto ret = check(curr, "point out of range");
	return &(*ret)[curr];
}

const string & ConstStrBlobPtr::operator*()const
{
	// TODO: 在此处插入 return 语句
	auto ret = check(curr, "point out of range");
	return (*ret)[curr];

}

const string * ConstStrBlobPtr::operator->()const
{
	auto ret = check(curr, "point out of range");
	return &(*ret)[curr];
}
14.31

因为StrBlobPtr内部管理资源的成员是weak_ptr一种智能指针,它指向了一个shared_ptr管理的对象,shard_ptr可以自动的管理内存,所以我们不必定义拷贝控制函数。

14.32

temp对象通过箭头访问符,访问StrBolbPtr的->访问符。strblobptr的箭头访问符,返回string类型的指针,通过该指针访问string的size()函数。

struct Temp
{
	StrBlobPtr* operator->();
	StrBlobPtr* p;
};

cout<<temp->operator->()->size()<<endl;
发布了54 篇原创文章 · 获赞 6 · 访问量 3294

猜你喜欢

转载自blog.csdn.net/zengqi12138/article/details/104537165