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;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章