第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;