練習14.1:在什麼情況下重載的運算符與內置運算符有所區別?在什麼情況下重載的運算符又與內置運算符一樣?
不同點:
重載運算符必須具有至少一個class或枚舉類型的操作數。
重載運算符不保證操作數的求值順序,例如對&&和||的重載版本不再具有“短路求值”的特性,兩個操作數都要求值,而且不規定操作數的求值順序。
相同點:
對於優先級和結合性級操作數的數目都不變。
練習14.2:為Sales_data編寫重載的輸入、輸出、加法和復合賦值運算符的聲明。
class Sales_data
{
friend std::istream& operator>>(std::istream&, Sales_data &);
friend std::ostream& operator<<(std::ostream&, const Sales_data&);
public:
Sales_data& operator+=(const Sales_data&);
};
Sales_data operator+(const Sales_data&, const Sales_data&);
(a) "cobble" == "stone" (b) svec1[0] ==svec2[0] (c)svec1 ==svec2 (d) svec[0] == "stone"
(a)應用了C++內置版本的==,比較兩個指針。(b) 應用了string版本的==。(c)應用了vector版本的==。(d)應用了string版本的==。
練習14.4:如何確定下列運算符是否應該是類的成員?
(a) % (b) %= (c) ++ (d) -> (e) << (f) && (g) == (h) ()
(a) %通常定義為非成員。
(b) %=通常定義為類成員,因為它會改變對象的狀態。
(c) ++通常定義為類成員,因為它會改變對象的狀態。
(d) ->必須定義為類成員,否則編譯報錯
(e) <<通常定義為非成員
(f) && 通常定義為非成員。
(g) ==通常定義為非成員。
(h) ()必須定義為類成員,否則編譯會報錯。
練習14.5:在7.5.1節的練習7.40中,編寫了下列類中的某一個框架,請問在這個類中應該定義重載的運算符嗎?如果是,請寫出來。
(a)Book (b)Date (c)Employee (d)Vehicle (e)Object (f)Tree
#includeusing std::ostream; using std::endl; class Date { public: Date() { } Date(int y, int m, int d) {year = y; month = m; day = d;} friend ostream& operator<<(ostream &os, const Date &dt); private: int year, month, day; }; ostream& operator<<(ostream& os, const Date& d) { const char sep = '\t'; os << "year:" << d.year << sep << "month:" << d.month << sep << "day:" << d.day << endl; return os; }
class Sales_data
{
friend ostream& operator<<(ostream &os, const Sales_data &item);
//其他成員
};
ostream& operator<<(ostream &os, const Sales_data &item)
{
const char *sep = ' ';
os << item.isbn() << sep << item.units_sold << sep << item.revenue <
練習14.7:你在13.5節的練習中曾經編寫了一個String類,為它定義一個輸出運算符。
class String
{
public:
String();
String(const char *str);
friend ostream& operator<<(ostream &os, const String &str);
private:
char *str;
};
ostream& operator<<(ostream &os, const String &str)
{
cout << str;
return os;
}
見練習14.5。
練習14.10:對於Sales_data的輸入運算符來說如果給定了下面的輸入將發生什麼情況?
(a)0-201-99999-9 10 24.95 (b) 10 24.95 0-210-99999-9
(a)參數中傳入的Sales_data對象將會得到輸入的值,其中bookNo、units_sold、price的值分別是0-201-99999-9、10、24.95,同時revenue的值是249.5.
(b)輸入錯誤,參數中傳入的Sales_data對象將會得到默認值。
練習14.11:下面的Sales_data輸入運算符存在錯誤嗎?如果有,請指出來。對於這個輸入運算符如果仍然給定上一個練習的輸入將發生什麼情況?
istream& operator>>(istream& in, Sales_data& s)
{
double price;
in >> s.bookNo >> s.unite_sold >> price;
s.revenue = s.unite_sold * price;
return in;
}
這個實現沒有判斷輸入數據的正確性,是錯誤的。
(a)如果輸入的是0-201-99999-9 10 24.95,程序不會報錯,Sales_data能得到正確的值。
(b)如果輸入的是10 24.95 0-201-99999-9,Sales_data會得到錯誤的值。
練習14.12:你在7.5.1節的練習中曾經選擇並編寫了一個類,為它定義一個輸入運算符並確保該運算符可以處理輸入錯誤。
#includeusing std::istream; class Date { public: Date() { } Date(int y, int m, int d) {year = y; month = m; day = d;} friend istream& operator>>(istream &is, Date &dt); private: int year, month, day; }; istream& operator>>(istream &is, Date &dt) { is >> dt.year >> dt.month >> dt.day; if (!is) dt = Date(0, 0, 0); return is; }
可以定義一個減法運算符
class Sales_data
{
friend Sales_data operator-(const Sales_data &lhs, const Sales_data &rhs);
public:
Sales_data& operator-=(const Sales_data &rhs);
//其他成員
};
Sales_data operator-(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sub = lhs;
sub -= rhs;
return sub;
}
Sales_data& Sales_data::operator-=(const Sales_data &rhs)
{
units_sold -= rhs.units_sold;
revenue -= rhs.revenue;
return *this;
}
從頭實現operator+的方式與借助operator+=實現的方式相比,在性能上沒有優勢,而可讀性上後者顯然更好。
練習14.15:你在7.5.1節的練習7.40中曾經選擇並編寫了一個類。你認為它應該含有其他算術運算符嗎?如果是,請實現它們;如果不是,解釋原因。
在練習7.40中,變寫了Date類,算術運算對Date沒有太大意義,不需要為Date重載算術運算符。
練習14.16:為你的StrBlob類、StrBlobPtr類、StrVec類和String類分別定義相等和不相等運算符。
//StrBlob
class StrBlob
{
friend bool operator==(const StrBlob &lhs, const StrBlob &rhs);
friend bool operator!=(const StrBlob &lhs, const StrBlob &rhs);
//其他成員
};
bool operator==(const StrBlob &lhs, const StrBlob &rhs)
{
return lhs.data ==rhs.data;
}
bool operator!=(const StrBlob &lhs, const StrBlob &rhs)
{
return !(lhs == rhs);
}
//StrBlobPtr
class StrBlobPtr
{
friend bool operator==(const StrBlobPtr &lhs, const StrBlobPtr &rhs);
friend bool operator!=(const StrBlobPtr &lhs, const StrBlobPtr &rhs);
//其他成員
};
bool operator==(const StrBlobPtr &lhs, const StrBlobPtr &rhs)
{
auto l = lhs.wptr.lock(), r = rhs.wptr.loc();
if (l == r)
return (!r || lhs.curr == rhs.curr);
else
return false;
}
bool operator!=(const StrBlobPtr &lhs, const StrBlobPtr &rhs)
{
return !(lhs == rhs);
}
//StrVec
class StrVec
{
friend bool operator==(const StrVec &lhs, const StrVec &rhs);
friend bool operator!=(const StrVec &lhs, const StrVec &rhs);
//其他成員
};
bool operator==(const StrVec &lhs, const StrVec &rhs)
{
if (lhs.size() == rhs.size())
return false;
for (auto itr1 = lhs.begin(), itr2 = rhs.begin(); itr1 != lhs.end() && itr2 != rhs.end(); ++itr1, ++itr2)
{
if (*itr1 != *itr2)
return false;
}
return true;
}
bool operator!=(const StrVec &lhs, const StrVec &rhs)
{
return !(lhs == rhs);
}
//String
class String
{
friend bool operator==(const String &lhs, const String &rhs);
friend bool operator!=(const String &lhs, const String &rhs);
//其他成員
private:
const char *str;
};
bool operator==(const String &lhs, const String &rhs)
{
return strcmp(lhs.str, rhs.str);
}
bool operator!=(const String &lhs, const String &rhs)
{
return !(lhs == rhs);
}
class Date
{
friend bool operator==(const Date &dl, const Date &d2);
friend bool operator!=(const Date &d1, const Date &d2);
//其他成員
};
bool operator==(const Date &d1, const Date &d2)
{
return d1.year == d2.year && d1.month == d2.month && d1.day == d2.day;
}
bool operator!=(const Date &d1, const Date &d2)
{
return !(da == d2);
}
class String
{
friend bool operator<(const String &s1, const String &s2);
friend bool operator<=(const String &s1, const String &s2);
friend bool operator>(const String &s1, const String &s2);
friend bool operator>=(const String &s1, const String &s2);
//其他成員
};
friend bool operator<(const String &s1, const String &s2)
{
return strcmp(s1.str, s2.str) < 0;
}
friend bool operator<=(const String &s1, const String &s2)
{
return strcmp(s1.str, s2.str) <= 0;
}
friend bool operator>(const String &s1, const String &s2)
{
return strcmp(s1.str, s2.str) > 0;
}
friend bool operator>=(const String &s1, const String &s2)
{
return strcmp(s1.str, s2.str) >= 0;
}
class StrBlob
{
friend bool operator<(const StrBlob &s1, const StrBlob &s2);
friend bool operator<=(const StrBlob &s1, const StrBlob &s2);
friend bool operator>(const StrBlob &s1, const StrBlob &s2);
friend bool operator>=(const StrBlob &s1, const StrBlob &s2);
};
bool operator<(const StrBlob &s1, const StrBlob &s2)
{
return *s1.data < *s2.data;
}
bool operator<=(const StrBlob &s1, const StrBlob &s2)
{
return *s1.data <= *s2.data;
}
bool operator>(const StrBlob &s1, const StrBlob &s2)
{
return *s1.data > *s2.data;
}
bool operator>=(const StrBlob &s1, const StrBlob &s2)
{
return *s1.data >= *s2.data;
}
class StrBlobPtr
{
friend operator<(const StrBlobPtr &s1, const StrBlobPtr &s2);
friend operator<=(const StrBlobPtr &s1, const StrBlobPtr &s2);
friend operator>(const StrBlobPtr &s1, const StrBlobPtr &s2);
friend operator>=(const StrBlobPtr &s1, const StrBlobPtr &s2);
};
bool operator<(const StrBlobPtr &s1, const StrBlobPtr &s2)
{
auto l = s1.wptr.lock(), r = s2.wptr.lock();
if (l == r)
{
if (!r)
return false;
return (s1.curr < s2.curr);
}
else
return false;
}
bool operator<=(const StrBlobPtr &s1, const StrBlobPtr &s2)
{
auto l = s1.wptr.lock(), r = s2.wptr.lock();
if (l == r)
return (!r || s1.curr <= s2.curr);
else
return false;
}
bool operator>(const StrBlobPtr &s1, const StrBlobPtr &s2)
{
auto l = s1.wptr.lock(), r = s2.wptr.lock();
if (l == r)
{
if (!r)
return false;
return (s1.curr > s2.curr);
}
else
return false;
}
bool operator>=(const StrBlobPtr &s1, const StrBlobPtr &s2)
{
auto l = s1.wptr.lock(), r = s2.wptr.lock();
if (l == r)
return (!r || s1.curr >= s2.curr);
else
return false;
}
class StrVec
{
friend operator<(const StrVec &s1, const StrVec &s2);
friend operator<=(const StrVec &s1, const StrVec &s2);
friend operator>(const StrVec &s1, const StrVec &s2);
friend operator>=(const StrVec &s1, const StrVec &s2);
//其他成員
};
bool operator<(const StrVec &s1, const StrVec &s2)
{
for (auto p1 = s1.begin(), p2 = s2.begin(); p1 != s1.end(), p2 != s2.end(); ++p1, ++p2)
{
if (*p1 < *p2)
return true;
else if (*p1 > *p2)
return false;
}
if (p1 == s1.end() && p2 != s2.end())
return true;
return false;
}
bool operator<=(const StrVec &s1, const StrVec &s2)
{
for (auto p1 = s1.begin(), p2 = s2.begin(); p1 != s1.end(), p2 != s2.end(); ++p1, ++p2)
{
if (*p1 < *p2)
return true;
else if (*p1 > *p2)
return false;
}
if (p1 == s1.end())
return true;
return false;
}
bool operator>(const StrVec &s1, const StrVec &s2)
{
for (auto p1 = s1.begin(), p2 = s2.begin(); p1 != s1.end(), p2 != s2.end(); ++p1, ++p2)
{
if (*p1 < *p2)
return false;
else if (*p1 > *p2)
return true;
}
if (p1 == s1.end() && p2 != s2.end())
return true;
return false;
}
bool operator>=(const StrVec &s1, const StrVec &s2)
{
for (auto p1 = s1.begin(), p2 = s2.begin(); p1 != s1.end(), p2 != s2.end(); ++p1, ++p2)
{
if (*p1 < *p2)
return false;
else if (*p1 > *p2)
return true;
}
if (p2 == s2.end())
return true;
return false;
}
練習14.19:你在7.5.1節的練習7.40中曾經選擇並編寫了一個類,你認為它應該含有關系運算符嗎?如果是,請實現它;如果不是,解釋原因。
class Date
{
friend operator<(const Date &d1, const Date &d2);
friend operator<=(const Date &d1, const Date &d2);
friend operator>(const Date &d1, const Date &d2);
friend operator>=(const Date &d1, const Date &d2);
//其他成員
};
bool operator<(const Date &d1, const Date &s2)
{
return (d1.year < d2.year) || (d1.year == d2. year && d1.month < d2.month) || (d1.year == d2.year && d1.month == d2.month && d1.day < d2.day);
}
bool operator<=(const Date &d1, const Date &s2)
{
return (d1 < d2) || (d1 == d2);
}
bool operator>(const Date &d1, const Date &s2)
{
return !(d1 <= d2);
}
bool operator>=(const Date &d1, const Date &s2)
{
return (d1 > d2) || (d1 == d2);
}
練習14.20:為你的Sales_data類實現加法和復合賦值運算符。
class Sales_data
{
friend Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs);
public:
Sales_data& operator+=(const Sales_data &rhs);
//其他成員
};
Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs;
sum += rhs;
return sum;
}
Sales_data& Sales_data::operator+=(const Sales_data &rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
在性能上沒有優勢,可讀性也不好。
class Sales_data
{
friend Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs);
public:
Sales_data& operator+=(const Sales_data &rhs);
//其他成員
};
Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs;
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return sum;
}
}
Sales_data& Sales_data::operator+=(const Sales_data &rhs)
{
*this = (*this) + rhs;
}
class Sales_data
{
public:
Sales_data& operator=(const string &isbn);
//其他成員
};
Sales_data& Sales_data::operator=(const string &isbn)
{
bookNo = isbn;
return *this;
)
class StrVec
{
public:
StrVec& operator=(std::initializer_list il);
//其他成員
};
StrVec& StrVec::operator=(std::initializer_list il)
{
auto data = alloc_n_copy(il.begin(), il.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
練習14.24:你在7.5.1節的臉7.40中曾經選擇並編寫了一個類,你認為它應該含有拷貝賦值和移動賦值運算符嗎?如果是,請實現它們。
再聯系7.40中,我們變細了Date類,它只有三個int類型的數據成員,淺拷貝就能滿足要求,因此不需要另外定義拷貝賦值和移動賦值運算符。
練習14.25:上題的這個類還需要定義其他賦值運算符嗎?如果是,請實現它們;同時說明運算對象應該是什麼類型並解釋原因。
class Date
{
public:
Date& operator=(const string &date);
//其他成員
};
Date& Sales_data::operator=(const string &date)
{
istringstream in(date);
char ch1, cha2;
in >> year >> ch1 >> month >> ch2 >> day;
if (!in || ch1 != '-' || ch2 != '-')
throw std::invalid_argument("Bad date");
if (month < 1 || month >12 || day < 1 || day > 31)
throw std::invalid_argument("Bad date");
return *this;
}
class StrBlob
{
public:
std:;string& operator[](std:;size_t n) { return data[n]; }
const std:;string& operator[](std:;size_t n) const { return data[n]; }
};
class StrBlobPtr
{
std::string& operator[](std::size_t n) { return (*wptr.lock())[n]; }
const std::string& operator[](std::size_t n) const { return (*wptr.lock())[n]; }
};
class StrVec
{
public:
std:;string& operator[])(std:;size_t n) { return elements[n]; }
const std:;string& operator[])(std:;size_t n) const { return elements[n]; }
};
class String
{
public:
char& operator[](std::size_t n) { return (char) str[n]; }
const char& operator[](std::size_t n) const { return (char) str[n]; }
private:
char *str;
}
class StrBlobPtr
{
public:
//前綴
StrBlobPtr& operator++();
StrBlobPtr& operator--();
//後綴
StrBlobPtr operator++(int);
StrBlobPtr operator--(int);
};
StrBlobPtr& StrBlobPtr::operator++()
{
check(curr, " increment past end of StrBlobPtr ");
++curr;
return *this;
}
StrBlobPtr& StrBlobPtr::operator--()
{
--curr;
check(-1, " decrement past begin of StrBlobPtr ");
return *this;
}
StrBlobPtr StrBlobPtr::operator++(int)
{
StrBlobPtr ret = *this;
++*this;
return ret;
}
StrBlobPtr StrBlobPtr::operator--(int)
{
StrBlobPtr ret = *this;
--*this;
return ret;
}
class StrBlobPtr
{
friend StrBlobPtr operator+(int n);
friend StrBlobPtr operator-(int n);
//其他成員
};
StrBlobPtr StrBlobPtr::operator+(int n)
{
auto ret = *this;
ret.curr += n;
return ret;
}
StrBlobPtr StrBlobPtr::operator-(int n)
{
auto ret = *this;
ret.curr -= n;
return ret;
}
對於++和--運算符,無論他是前綴版本還是後綴版本,都會改變對象本身的值,因此不能定義成const的。
練習14.30:為你的StrBlob類和在12.1.6節練習12.22中定義的ConstStrBlobPtr類分別添加解引用運算符和箭頭運算符。注意:因為ConstStrBlobPtr的數據成員指向const vector,所以ConstStrBlobPtr中的運算符必須返回常量引用。
class StrBlobPtr
{
public:
std::string& operator*() const
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
std::string* operator->() const
{
return &(this->operator*());
}
};
class ConstStrBlobPtr
{
public:
const std::string& operator*() const
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
const std::string* operator->() const
{
return &(this->operator*());
}
};
練習14.31:我們的StrBlobPtr類沒有定義拷貝構造函數、賦值運算符和析構函數,為什麼?
對於StrBlobPtr類,它的數據成員有兩個,分別是weak_ptr
練習14.32:定義一個類令其含有指向StrBlobPtr對象的指針,為這個類定義重載的建投運算符。
class MyClass
{
public:
std::string* operator->() const
{
return ptr->operator->();
}
private:
StrBlobPtr *ptr;
}
0個或多個。
練習14.34:定義一個函數對象類,伶氣質型if-then-else的操作:該類的調用運算符接受三個形參,它首先檢查第一個形參,如果成功返回第二個形參的值,如果不成功返回第三個形參的值。
class IfElseThen
{
public:
IfElseThen() { }
IfElseThen(int i1, int i2, int i3) : iVal1(i1), iVal2(i2), iVal3(i3) { }
int operator()(int i1, int i2, int i3)
{
return i1 ? i2 : i3;
}
private:
int iVal1, iVal2, iVal3;
};
class ReadString
{
public:
ReadString(istream &is = cin) : is(is) { }
std:;string operator()()
{
string line;
if (!getline(is, line))
{
line = " ";
}
return line;
}
private:
istream &is;
};
void testReadString()
{
ReadString rs;
vector vec;
while (true)
{
string line = rs();
if (!line.empty())
{
vec.push_back(line);
}
else
break;
}
}
class IntCompare
{
public:
IntCompare(int v) : val(v) { }
bool operator()(int v) { return val ==v; }
private:
int val;
};
int main()
{
vector vec = {1, 2, 3, 2, 1};
const int oldValue = 2;
const int newValue = 200;
IntCompare icmp(oldValue);
std::replace_if(vec.begin(), vec.end(), icmp, newValue);
return 0;
}
#include#include #include #include using std::istream; using std::cout; using std::cin; using std::endl; using std::vector; using std::string; class StrLenIs { public: StrLenIs(int len) : len(len) { } bool operator()(const string &str) { return str.length() == len; } private: int len; }; void readStr(istream &is, vector &vec) { string word; while (is >> word) { vec.push_back(word); } } int main() { vector vec; readStr(cin, vec); const int minLen = 1; const int maxLen = 10; for (int i = minLen; i <= maxLen; ++i) { StrLenIs slenIs(i); cout << "len: " << i << ", cnt: " << count_if(vec.begin(), vec.end(), slenIs) << endl; } return 0; }
練習14.39:修改上一題的程序令其報告長度在1至9之間的單詞有多少個、長度在10以上的單詞又有多少個。
#include#include #include #include using std::istream; using std::cout; using std::cin; using std::endl; using std::vector; using std::string; class StrLenBetween { public: StrLenBetween(int minLen, int maxLen) : minLen(minLen), maxLen(maxLen) { } bool operator()(const string &str) { return str.length() >= minLen && str.length() <= maxLen; } private: int minLen, maxLen; }; class StrNoShorterThan { public: StrNoShorterThan(int len) : minLen(len) { } bool operator()(const string &str) { return str.length() >= minLen; } private: int minLen; }; void readStr(istream &is, vector &vec) { string word; while (is >> word) { vec.push_back(word); } } int main() { vector vec; readStr(cin, vec); StrLenBetween slenBetween(1, 9); StrNoShorterThan sNoShorterThan(10); cout << "len 1-9 :" << count_if(vec.begin(), vec.end(), slenBetween) << endl; cout << "len >= 10 : " << count_if(vec.begin(), vec.end(), sNoShorterThan) << endl; return 0; }
練習14.40:重新編寫10.3.2節的biggies函數,使用函數對象替換其中的lambda表達式。
class IsShorter
{
public:
bool operator()(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
};
class NotShorterThan
{
public:
NotShorterThan(int len) : minLen(len) { }
bool operator()(const string &str)
{
return str.size() >= minLen;
}
private:
int minLen;
};
class PrintString
{
public:
void operator()(const string &str)
{
cout << str << " ";
}
};
void biggies(vector &words, vector::size_type sz)
{
elimDups(words);
IsShorter is;
stable_sort(words.begin(), words.end(), is);
NotShorterThan nst(sz);
auto wc = find_if(words.begin(), words.end(), nst);
auto count = words.end() - wc;
cout << count << " " << make_plural(count, "words", "s") << " of length " <, sz << " or longer" <
練習14.41:你認為C++11新標准為什麼要增加lambda?對於你自己來說,什麼情況下會使用lambda,什麼情況下會使用類?
在C++11中,lambda是通過匿名的函數對象來實現的,因此我們可以把lambda看作是對函數對象在使用方式上進行的簡化。當代碼需要一個簡單的函數,並且這個函數並不會在其他地方被使用時,就可以使用lambda來實現,此時它所起的作用類似於匿名函數。但如果這個函數需要多次使用,並且它需要保存某些狀態的話,使用函數對象更合適一些。
練習14.42:使用標准庫函數對象及適配器定義一條表達式,令其
(a)統計大於1024的值有多少個。
(b)找到第一個不等於pooh的字符串。
(c)將所有的值乘以2.
count_if(vec.begin(), vec.end(), bind2nd(greater(), 1024)); find_if(vec.begin(), vec.end(), bind2nd(not_equal_to (), "pooh")); transform(vec.begin(), vec.end(), vec.begin(), bind2nd(multiplies (), 2));
bool divideByAll(vector&ivec, int dividend) { return count_if(ivec.begin(), ivec.end(), bindlst(modulus , dividend)) == 0; }
#include#include
如果要轉換成string,那麼返回值應該是bookNo。
如果要轉換成double,那麼返回值應該是revenue。
練習14.46:你認為應該為Sales_data類定義上面兩種類型轉換運算符嗎?應該把它們聲明成explicit的嗎?為什麼?
Sales_data不應該定義這兩種類型轉換運算符,因為對於類來說,它包含三個數據成員:bookNo,units_sold和revenue,只有三者在一起才是有效的數據。但是如果確實想要定義這兩個類型轉換運算符的話,應該把它們聲明成explicit的,這樣可以防止sales_data 在默寫情況下被默認轉換成string或double類型,這有可能導致意料之外的運算結果。
練習14.47:說明下面這兩個類型轉換運算符的區別。
struct Integral
{
operator const int();
operator int() const;
};
前者將對象轉換成const int,在接受const int值的地方才能夠使用。
後者將對象轉換成int值,相對來說更加通用一些。
練習14.48:你在7.5.1節的練習7.40中曾經選擇並編寫了一個類,你認為它應該含有bool的類型轉換運算符嗎?如果是,解釋原因並說明該運算符是否應該是explicit的;如果不是,也請解釋原因。
之前我們編寫了Date類,它含有3個數據成員:year、month和day。
我們可以為Date提供一個bool類型的轉換運算符,用來檢查3個數據成員是否都是有效值,bool類型轉換運算符應該聲明為explicit的,因為我們是有意要在條件表達式中使用它的。
練習14.49:為上一題提到的類定義一個轉換目標是bool的類型轉換運算符,先不用在意這麼做是否應該。
class Date
{
explicit operator bool()
{
vector> days_per_month = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
return 1 <= month && month <= 12 && 1 <= day && day <= days_per_month[isLeapYear()? 1 : 0][month - 1];
}
bool isLeapYear()
{
return (year % 4 ==0 && year % 100 != 0) || (year % 400 == 0);
}
};
struct LongDouble {
LongDouble(double = 0.0);
operator double();
operator float();
};
LongDouble ldObj;
int ex1 = ldObj;
float ex2 ldObj;
對於int ex1 = ldOb;,它需要把LongDouble類型轉換成int類型,但是LongDouble並沒有定義對應的類型轉換運算符,因此它會嘗試使用其他的來進行轉換。題中給出的兩個都滿足需求,但編譯器無法確定那一個更合適,因此會產生二義性錯誤。
對於foloat ex2 = ldObj;,它需要把LongDouble轉換成float類型,而我們恰好定義了對應的類型轉換運算符,因此直接調用operator float()即可。
練習14.51:在調用calc的過程中,可能用到哪些類型轉換序列呢?說明最佳可行函數是如何選拔出來的。
void calc(int);
void calc(LongDouble);
double dval;
calc(dval);
這裡會優先調用void calc(int)函數。因為double轉換為int是標准類型轉換,而轉換為LongDouble則是轉換為用戶自定義類型,實際上調用了轉換構造函數,因此前者優先。
練習14.52:在下面的加法表達式中分別選用了哪個operator?列出候選函數、可行函數及為每個可行函數的實參執行的類型轉換。
struct longDouble {
//用於演示的成員operator+; 在通常情況下+s是個非成員
longDouble operator+(const SmallInt&);
//其他成員與14.9.2節一致
};
longDouble operator+(longDouble&, double);
SmallInt si;
longDouble ld;
ld = si + ld;
ld = ld + si;
對於ld=si+ld,由於LongDouble不能轉換為SmallInt,因此Smallint的成員operator+和friend operator都不可行。
由於Smallint不能轉換為LongDouble,LongDouble的成員operator+和非成員operator+也都不可行。
由於SmallInt可以轉換為int, LongDouble了可以轉換為float和double,所以內置的operator+(int, float)和operator+(int, double)都可行,會產生二義性。
對於ld=ld+si,類似上一個加法表達式,由於Smallint不能轉換為double,LongDouble也不能轉換為SmallInt,因此SmallInt的成員operator+和兩個非成員operator+都不匹配。
LongDouble的成員operator+可行,且為精確匹配。
SmallInt可以轉換為int,longDouble可以轉換為float和double,因此內置的operator+(float, int)和operator(double, int)都可行。但它們都需要類型轉換,因此LongDouble的成員operator+優先匹配。
練習14.53:假設我們已經定義了如第522頁所示的SmallInt,判斷下面的加法表達式是否合法。如果合法,使用了哪個加法運算符?如果不合法,應該怎樣修改代碼才能使其合法?
SamllInt sl;
double d = s1 + 3.14;
內置的operator+(int, double)是可行的,而3.14可以轉換為int,然後再轉換為SmallInt,所以SmallInt的成員operator+也是可行的。兩者都需要進行類型轉換,所以會產生二義性。改為:double d = s1 +Smallint(3.14);即可。