14.1 在什么情况下重载的运算符与内置运算符有所区别?在什么情况下重载的运算符又与内置运算符一样?
区别
- 我们可以直接通过对象调用一个重载运算符,如data1.operator+(data2)。
- 重载运算符必须是一个类的成员或者至少有一个参数是类类型。
- 某些运算符指定了运算对象的求值顺序或短路求值属性,这些规则我们无法应用到重载运算符上,比如逻辑运算符、关系运算符和逗号运算符。
相同
- 重载运算符的优先级和结合律与对应的内置运算符保持一致。
14.2 为Sales_data编写重载的输入、输出、加法和复合赋值运算符。
istream& operator>> (istream& is, Sales_data& s)
{
double price;
is >> s.bookNo >> s.units_sold >> price;
s.revenue = price * s.units_sold;
return is;
}
ostream& operator<< (ostream& os, Sales_data& s)
{
os << s.isbn() << " " << s.units_sold << " " << s.revenue << " " << s.avg_price() << endl;
}
Sales_data& operator+ (const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data ret(lhs);
ret += rhs;
return ret;
}
Sales_data& Sales_data::operator+= (const Sales_data& s)
{
units_sold += s.units_sold;
revenue += s.revenue;
return *this;
}
14.3 string和vector都定义了重载的以比较各自的对象,假设svec1和svec2是存放string的vector,确定在下面的表达式中分别使用了哪个版本的?
(a) "cobble" == "stone" // string重载的“==”
(b) svec1[0] == svec2[0] // string重载的“==”
(c) svec1 == svec2 // vector重载的“==”
(d) "svec1[0] == "stone" // string重载的“==”
14.4 如何确定下列运算符是否应该是类的成员?
(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
#include <iostream>
class Date
{
friend std::istream& operator>> (std::istream& is, Date& d);
friend bool operator> (Date& d1, Date& d2);
friend bool operator< (Date& d1, Date& d2);
friend bool operator== (Date& d1, Date& d2);
public:
Date(){}
Date(int y, int m, int d) : year(y), month(m), day(d) { }
int getYear() { return year; }
int getMonth() { return month; }
int getDay() { return day; }
private:
int year;
int month;
int day;
};
std::ostream& operator<< (std::ostream& os, Date& d)
{
os << d.getYear() << "-" << d.getMonth() << "-" << d.getDay() << std::endl;
return os;
}
std::istream& operator>> (std::istream& is, Date& d)
{
is >> d.year >> d.month >> d.day;
return is;
}
bool operator> (Date& d1, Date& d2)
{
if (d1.year > d2.year) {
return true;
}
else if ((d1.year == d2.year) && (d1.month > d2.month)) {
return true;
}
else if ((d1.month == d2.month) && (d1.day > d2.day)) {
return true;
}
else {
return false;
}
}
bool operator== (Date& d1, Date& d2)
{
if ((d1.year == d2.year) && (d1.month == d2.month) && (d1.day == d2.day)){
return true;
}
else {
return false;
}
}
14.6 为你的Sales_data类定义输出运算符。
见14.2题
14.7 你在13.5节的练习中曾经编写了一个String类,为它定义一个输出运算符。
ostream& operator<< (ostream& os, String& s)
{
os << s.begin << endl;
}
14.8 你在7.51节的练习7.40中曾经编写了一个类,为它定义一个输出运算符。
见14.5题
14.9 为你的Sales_data类定义输入运算符。
istream& operator>> (istream& is, Sales_data& s)
{
double price;
is >> s.bookNo >> s.units_sold >> price;
if (is) {
s.revenue = price * s.units_sold;
}
else {
s = Sales_data();
}
return is;
}
14.10 对于Sales_data的输入运算符来说如果给定了下面的输入将发生什么情况?
(a) 0-201-99999-9 10 24.95
// 输出正确
0-201-99999-9 10 249.5 24.95
(b) 10 24.95 0-210-99999-9
// 数据错乱,输出错误
10 24 22.8 0.95
14.11 下面的Sales_data输入运算符存在错误吗?如果有,请指出来。对于这个输入运算符如果仍然给定上个练习的输入将发生什么情况?
istream& operator>>(istream& in, Sales_data& s)
{
double price;
in >> s.bookNo >> s.units_sold >> price;
s.revenue = s.units_sold * price;
return in;
}
这种写法不会检查输入流是否正确,练习上题的输入不会发生什么。
14.12 你在7.51节的练习7.40中曾经编写了一个类,为它定义一个输入运算符并确保该运算符可以处理输入错误。
std::istream& operator>> (std::istream& is, Date& d)
{
is >> d.year >> d.month >> d.day;
if (is) {
return is;
}
else {
d = Date();
return is;
}
}
14.13 你认为Sales_data类还应该支持哪些其他算术运算符?如果有的话,请给出它们的定义。
// 还应支持“-”运算符
Sales_data& Sales_data::operator-= (const Sales_data& s)
{
units_sold -= s.units_sold;
revenue -= s.revenue;
return *this;
}
Sales_data operator- (const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data ret (lhs);
ret -= rhs;
return ret;
}
14.14 你觉得为什么调用operator+=来定义operator+比其他方法更有效?
因为使用+=不需要再分配临时空间,而如果使用+来实现,在进行加法运算时也会申请临时空间保存等号右边的和。例如:
Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; // copy data members from lhs into sum sum += rhs; // add rhs into sum return sum; }
14.15 你在7.40题中曾经选择并编写了一个类,你认为它应该含有其他算数运算符吗?如果是,请实现它们;如果不是,解释原因。
我定义的Date类,我认为其不应该含有算数运算符,因为对日期进行+、-、*、/没有意义。比如1992.11.28+1994.12.11有何含义?
14.16 为你的StrBlob类、StrBlobPtr类、StrVec类和String类分别定义相等运算符和不相等运算符。
// StrBlob
bool operator== (const StrBlob& lhs, const StrBlob& rhs)
{
return *lhs.data == *rhs.data;
}
bool operator!= (const StrBlob& lhs, const StrBlob& rhs)
{
return *lhs.data != *rhs.data;
}
// StrBlobPtr
bool operator== (const StrBlobPtr& lhs, const StrBlobPtr& rhs)
{
return lhs.curr == rhs.curr;
}
bool operator!= (const StrBlobPtr& lhs, const StrBlobPtr& rhs)
{
return lhs.curr != rhs.curr;
}
// StrVec
bool operator== (const StrVec& lhs, const StrVec& rhs)
{
if (lhs.size() != rhs.size()) {
return false;
}
else {
auto l_iter = lhs.begin();
auto r_iter = rhs.begin();
for (l_iter, r_iter; l_iter != lhs.end(); ++ l_iter, ++ r_iter) {
if (*l_iter != *r_iter) {
return false;
}
}
}
return true;
}
bool operator!= (const StrVec& lhs, const StrVec& rhs)
{
return !(lhs == rhs);
}
// String
bool operator== (const String& lhs, const String& rhs)
{
if (lhs.size() != rhs.size()) {
return false;
}
else {
auto l_iter = lhs.begin, r_iter = rhs.begin;
for (l_iter, r_iter; l_iter!=lhs.end; ++l_iter, ++r_iter) {
if (*l_iter != *r_iter) {
return false;
}
}
}
return true;
}
bool operator!= (const String& lhs, const String& rhs)
{
return !(lhs == rhs);
}
14.17 你在7.40题中曾经选择并编写了一个类,你认为它应该含有相等运算符吗?如果是,请实现它们;如果不是,解释原因。
Date类中应该含有相等运算符,其实现见14.5题。
14.18 为你的StrBlob类、StrBlobPtr类、StrVec类和String类分别定义关系运算符。
// StrBlob类
bool operator< (const StrBlob& lhs, const StrBlob& rhs)
{
return std::lexicographical_compare(lhs.data->begin(), lhs.data->end(), rhs.data->begin(), rhs.data->end());
}
bool operator> (const StrBlob& lhs, const StrBlob& rhs)
{
return !(lhs < rhs);
}
// StrBlobPtr类
bool operator> (const StrBlobPtr& lhs, const StrBlobPtr& rhs)
{
return lhs.curr > rhs.curr;
}
bool operator< (const StrBlobPtr& lhs, const StrBlobPtr& rhs)
{
return lhs.curr < rhs.curr;
}
// StrVec类
bool operator< (const StrVec& lhs, const StrVec& rhs)
{
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
bool operator> (const StrVec& lhs, const StrVec& rhs)
{
return rhs < lhs;
}
// String类
bool operator< (const String& lhs, const String& rhs)
{
return std::lexicographical_compare(lhs.begin, lhs.end, rhs.begin, rhs.end);
}
bool operator> (const String& lhs, const String& rhs)
{
return rhs < lhs;
}
14.19 你在7.40题中曾经选择并编写了一个类,你认为它应该含有关系运算符吗?如果是,请实现它们;如果不是,解释原因。
Date类应该含有关系运算符,比较两个日期的大小是合理的。实现请看14.5题。
14.20 为你的Sales_data类定义加法和复合赋值运算符。
见14.2题。
14.21 编写Sales_data类的+和+=运算符,使得+执行实际的加法操作而+=调用+。相比于14.3节和14.4节对这两个运算符的定义,本题的定义有何缺点?试讨论之。
Sales_data& Sales_data::operator+=(const Sales_data &rhs)
{
Sales_data temp = *this;
*this = temp + rhs;
return *this;
}
Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum;
sum.units_sold = lhs.units_sold + rhs.units_sold;
sum.revenue = lhs.revenue + rhs.revenue;
return sum;
}
实现“+”只使用+,实现+=时使用+,都需要多定义一个临时变量,而这是可以避免的。
14.22 定义赋值运算符的一个新版本,使得我们能把一个表示ISBN的string赋给一个Sales_data对象。
Sales_data& Sales_data::operator=(const std::string& isbn)
{
*this = Sales_data(isbn);
return *this;
}
14.23 为你的StrVec类定义一个initializer_list赋值运算符。
StrVec& StrVec::operator=(std::initializer_list<std::string> il)
{
auto data = alloc_n_copy(il.begin(), il.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
14.24 你在7.40题中曾经选择并编写了一个类,你认为它应该含有拷贝赋值和移动赋值运算符吗?如果是,请实现它们;如果不是,解释原因。
Date类不需要实现拷贝赋值和移动赋值运算符,因为Date类中只含有三个int变量,默认的浅拷贝就能满足要求。
14.25 上题的这个类还需要定义其他赋值运算符吗?如果是,请实现它们;同时说明运算对象应该是什么类型并解释原因。
还应该定义赋值运算符,参数为string,将date作为一个字符串赋值给Date类。
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;
}
14.26 为你的StrBlob类、StrBlbPtr类、StrVec类和String类定义下标运算符。
// String类
char String::operator[] (std::size_t n)
{
if (n < 0 || n > size()) {
cout << "out of range." << endl;
return 0;
}
return begin[n];
}
const char String::operator[] (std::size_t n) const
{
if (n < 0 || n > size()) {
cout << "out of range." << endl;
return 0;
}
return begin[n];
}
// StrVec类
std::string& operator[] (std::size_t n)
{
return elements[n];
}
const std::string& operator[] (std::size_t n) const
{
return elements[n];
}
14.27 为你的StrBlobPtr类添加递增和递减运算符。
// 前置递增和递减
StrBlobPtr& operator++ ()
{
check(curr, "increment past end of StrBlobPtr");
++ curr;
return *this;
}
StrBlobPtr& operator-- ()
{
-- curr;
check(curr, "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;
}
void test()
{
StrBlob sb1 = {"a", "b", "c"};
StrBlobPtr sbp1 (sb1, 0);
cout << sbp1.operator ++ (0).deref()<< endl; // 后置,a
cout << sbp1.operator ++().deref() << endl; // 前置,c
}
14.28 为你的StrBlobPtr类添加加法和减法运算符,使其可以实现指针的算术运算。
inline StrBlobPtr& StrBlobPtr::operator+=(size_t n)
{
curr += n;
check(curr, "increment past end of StrBlobPtr");
return *this;
}
inline StrBlobPtr& StrBlobPtr::operator-=(size_t n)
{
curr -= n;
check(curr, "increment past end of StrBlobPtr");
return *this;
}
inline StrBlobPtr StrBlobPtr::operator+(size_t n) const
{
StrBlobPtr ret = *this;
ret += n;
return ret;
}
inline StrBlobPtr StrBlobPtr::operator-(size_t n) const
{
StrBlobPtr ret = *this;
ret -= n;
return ret;
}
14.29 为什么不定义const版本的递增和递减运算符?
递增和递减运算符需要改变对象,因此不能定义为const。
14.30 为你的StrBlobPtr类和练习12.22中定义的ConstStrBlobPtr类分别添加解引用运算符和箭头运算符。注意:因为ConstStrBlobPtr的数据成员指向const vector,所以ConstStrBlobPtr中的运算符必须返回常量引用。
// StrBlobPtr
std::string& operator* () const
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
std::string* operator->() const
{
return &(this->operator*());
}
// ConstStrBlobPtr
inline const string* ConstStrBlobPtr::operator->() const
{
return &this->operator*();
}
inline ConstStrBlobPtr& ConstStrBlobPtr::operator++()
{
check(curr, "increment past end of ConstStrBlobPtr");
++curr;
return *this;
}
14.31 我们的StrBlobPtr类没有定义拷贝构造函数、赋值运算符及析构函数,为什么?
因为在这个类中我们不需要申请或管理内存,因此使用合成版本就足够了。
14.32 定义一个类令其含有指向StrBlobPtr对象的指针,为这个类定义重载的箭头运算符。
class StrBlobPtr_pointer
{
public:
StrBlobPtr_pointer() = default;
StrBlobPtr_pointer(StrBlobPtr* p) : pointer(p) { }
StrBlobPtr& operator *() const;
StrBlobPtr* operator->() const;
private:
StrBlobPtr* pointer = nullptr;
};
StrBlobPtr&
StrBlobPtr_pointer::operator *() const
{
return *pointer;
}
StrBlobPtr*
StrBlobPtr_pointer::operator ->() const
{
return pointer;
}
int main()
{
StrBlob sb{ "hello", "world" };
StrBlobPtr iter = sb.begin();
StrBlobPtr_pointer p(&iter);
std::cout << p->deref() << std::endl;
}
14.33 一个重载的函数调用运算符应该接受几个运算对象?
重载运算符函数的参数数量和该运算符作用的运算对象的数量一样多。函数调用运算符()最多可以传256个参数,因此在重载时,最多也能接受256个参数用作运算。
14.34 定义一个函数对象类,令其执行if-then-else的操作:该类的调用运算符接受三个形参,它首先检查第一个形参,如果成功返回第二个形参的值;如果不成功返回第三个形参的值。
class MyClass
{
public:
MyClass (int n) : num(n) {}
int operator() (int x, int y, int z);
private:
int num;
};
int MyClass::operator() (int x, int y, int z)
{
return (x == num) ? y : z;
}
int main()
{
MyClass mc(10);
int x = mc(9, 5, 20);
cout << x << endl;
}
14.35 编写一个类似于PrintString的类,令其从istream中读取一行输入,然后返回一个表示我们所读内容的string。如果读取失败,返回空string。
class PrintString
{
public:
PrintString (std::istream& i) : is (i) { std::getline (is, s); }
std::string& operator() ()
{
return s;
}
private:
std::istream& is;
std::string s;
};
void test1435()
{
PrintString ps(cin);
string s = ps();
cout << s << endl;
}
14.36 使用前一个练习定义的类读取标准输入,将每一行保存为vector的一个元素。
class PrintString
{
friend std::ostream& operator<< (std::ostream& os, PrintString& ps);
public:
PrintString (std::istream& i) : is (i)
{
std::string s;
while (std::getline (is, s)) {
svec.push_back(s);
}
}
private:
std::istream& is;
std::vector<std::string> svec;
};
std::ostream& operator<< (std::ostream& os, PrintString& ps)
{
for (auto i : ps.svec) {
os << i << " ";
}
os << std::endl;
return os;
}
void test1436()
{
PrintString ps(cin);
cout << ps ;
}
14.37 编写一个类令其检查两个值是否相等。使用该对象及标准库算法编写程序,令其替换某个序列中具有给定值的所有实例。
class IsEqual
{
public:
IsEqual (int n) : assign_val(n) {}
bool operator() (int num)
{
return assign_val == num;
}
private:
int assign_val;
};
void test1437()
{
vector<int> ivec = {1, 2, 3, 4, 2, 5};
replace_if(ivec.begin(), ivec.end(), IsEqual(2), 10);
for (auto i : ivec) {
cout << i << " ";
}
cout << std::endl;
}
14.38 编写一个类令其检查某个给定的string对象的长度是否与一个阈值相等。使用该对象编写程序,统计并报告在输入的文件中长度为1的单词有多少个、长度为2的单词有多少个、......、长度为10的单词有多少个。
class IsInRange
{
public:
IsInRange (std::size_t n) : sz (n) {}
bool operator() (const std::string& s)
{
return s.size() == sz;
}
size_t getSize() { return sz; }
private:
std::size_t sz;
};
void test1428()
{
vector<IsInRange> predicates;
map<size_t, size_t> len_cnt_table;
for (size_t i = 1; i < 11; ++i) {
len_cnt_table[i] = 0;
predicates.push_back(IsInRange(i));
}
ifstream fi ("C:/Users/tutu/Documents/code/cpp_primer/ch14/storyDataFile.txt");
string word;
while (fi >> word) {
for (auto i : predicates) {
if (i(word)) {
++ len_cnt_table[i.getSize()];
}
}
}
for (auto i : len_cnt_table) {
cout << i.first << " " << i.second << endl;
}
}
14.39 修改上一题的程序令其报告长度在1至9之间的单词有多少个、长度在10以上的单词有多少个。
class IsInRange
{
public:
IsInRange (std::size_t lowest, std::size_t biggest) : _lowest(lowest), _biggest(biggest) {}
bool operator() (const std::string& s)
{
return (s.size() <= _biggest) && (s.size() >= _lowest);
}
std::size_t getLowest() { return _lowest; }
std::size_t getBiggest() { return _biggest; }
private:
std::size_t _lowest;
std::size_t _biggest;
};
void test1429()
{
vector<IsInRange> predicates;
map<string, size_t> len_cnt_table;
predicates.push_back(IsInRange(1, 9));
predicates.push_back(IsInRange(10, 20));
ifstream fi ("C:/Users/tutu/Documents/code/cpp_primer/ch14/storyDataFile.txt");
string word;
while (fi >> word) {
for (auto i : predicates) {
if (i(word)) {
++ len_cnt_table[std::to_string(i.getLowest())+"-"+std::to_string(i.getBiggest())];
}
}
}
for (auto i : len_cnt_table) {
cout << "The length of word in range " << i.first << " is " << i.second << endl;
}
}
14.40 重新编写10.3.2节的biggies函数,使用函数对象类替换其中的lambda表达式。
class compareSize
{
public:
compareSize() {}
compareSize(std::size_t len) : sz(len) {}
bool operator() (const std::string& lhs, const std::string& rhs)
{
std::cout << "operator() (const std::string& lhs, const std::string& rhs)" << std::endl;
return lhs.size() >= rhs.size();
}
bool operator() (const std::string& s)
{
std::cout << "operator() (const std::string& s)" << std::endl;
return s.size() > sz;
}
private:
std::size_t sz;
};
void biggies(vector<string>& words, size_t sz)
{
elimDups(words);
printVec(words);
stable_sort(words.begin(), words.end(), compareSize());
auto wc = partition(words.begin(), words.end(), compareSize(sz));
printVec(words);
}
14.41 你认为c++11新标准为什么要增加lambda?对于你自己来说,什么情况下会使用lambda,什么情况下会使用类?
增加lambda使一些程序变得简单。
当一个函数不会被频繁的调用,而且实现也不复杂时,我倾向使用lambda。反之,若函数实现较复杂且要被频繁调用,那就使用类实现。
14.42 使用标准库函数对象及适配器定义一条表达式,令其
(a)统计大于1024的值有多少个。
(b)找到第一个不等于pooh的字符串。
(c)将所有的值乘以2。
using namespace std::placeholders;
// using std::placeholders; // error: namespace 'std::placeholders' not allowed in using-declaration
void test1442()
{
vector<int> ivec = {1, 11, 112, 1122, 11122};
size_t count = std::count_if(ivec.begin(), ivec.end(), bind(greater<int>(), _1, 1024));
cout << count << endl;
vector<string> svec = {"pooch", "apple", "banana", "pooch"};
auto word = find_if (svec.cbegin(), svec.cend(), bind(not_equal_to<string>(), _1, "pooch"));
cout << *word << endl;
transform(ivec.begin(), ivec.end(), ivec.begin(), bind(multiplies<int>(), _1, 2));
for (auto i : ivec) {
cout << i << " ";
}
cout << endl;
}
14.43 使用标准库函数对象判断一个给定的int值是否能被int容器中的所有元素整除。
void test1443(int num)
{
vector<int> ivec = {2, 4, 6, 8};
std::modulus<int> mod;
auto is_divisible = any_of(ivec.begin(), ivec.end(), [&mod, &num](int i)->bool{ return 0 == mod(num, i);});
cout << (is_divisible ? "Yes!" : "No!") << endl;
}
14.44 编写一个简单的桌面计算器使其能处理二元运算。
int add(int i, int j) { return i+j; };
auto mod = [](int i, int j) { return i%j; };
struct divide
{
int operator() (int dividend, int divisor) { return dividend / divisor; }
};
map<string, function<int(int, int)>> binops = {
{"+", add},
{"-", std::minus<int>()},
{"*", [](int i, int j) { return i*j; }},
{"/", divide()},
{"%", mod}
};
void test1444()
{
while (1) {
cout << "please input as 2 * 5 : ";
int lhs, rhs;
string op;
cin >> lhs >> op >> rhs;
cout << binops[op](lhs, rhs) << endl;
}
}
14.45 编写类型转换运算符将一个Sales_data对象分别转换成string和double,你认为这些运算符的返回值应该是什么?
explicit operator std::string() const { return bookNo; }
explicit operator double() const { return revenue; }
int main()
{
Sales_data sd("Sb1001", 2, 25.1);
cout << sd;
double price = static_cast<double>(sd);
cout << price << endl;;
string bookNo = static_cast<string>(sd);
cout << bookNo << endl;
return 0;
}
// 定义运算符转化没有返回值。
14.46 你认为应该为Sales_data类定义上面两种类型转换运算符吗?应该把它们声明成explicit的吗?为什么?
我觉得应该定义这两种转换,给使用者提供一种容易的途径去获取编号或价格。应该要声明为explicit,避免使用者无意中隐式转换从而产生意外的结果。
14.47 说明下面这两个类型转换运算符的区别。
struct Integal {
operator const int();
operator int() const;
};
// 前者意为转化为const int类型。
// 后者const意为在函数内不改变对象的值,将对象转化为int类型。
14.48 你在7.40练习中曾经选择并编写了一个类,你认为它应该含有向bool类型转换运算符吗?如果是,解释原因并说明该运算符是否应该是explicit的;如果不是,也请解释原因。
bool isLeapYear() const { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); }
explicit operator bool() const
{
vector<vector<int>> days_per_month = {{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 28, 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类型转换运算符,可以用来检验date的值是否合法。
// 应该是explict,避免使用者无意中隐式转换从而产生意外的结果。
14.49 为上一题提到的类定义一个转换目标是bool的类型转换运算符,先不用在意这么做是否应该。
如上题。
14.50 在初始化ex1和ex2的过程中,可能用到哪些类类型的转换序列呢?说明初始化是否正确并解释原因。
struct LongDouble {
LongDouble (double = 0.0);
operator double();
operator float();
};
LongDouble ldObj;
int ex1 = ldObj; // 在将ldObj转换为int时,类定义的类型转换都无法精准匹配,因此会产生二义性,可能先执行 operator double(), 再进行double到int的转换。
// 也可能调用operator float(),再进行float到int的转换。
float ex2 = ldObj; // 调用operator float()
14.51 在调用calc的过程中,可能用到哪些类型转换序列呢?说明最佳可行函数是如何被选出来的。
void calc(int);
void calc(LongDouble);
double dval;
calc(dval); // 哪个calc?
会优先调用calc(int),因为doube转int是标准类型转换,而LongDouble转int是用户自定义转换。
14.52 在下面的加法表达式中分别选用了哪个operator+?列出候选函数、可行函数及为每个可行函数的实参执行的类型转换:
struct LongDouble {
LongDouble operator+ (const SmallInt&); // 1
};
LongDouble operator+(LongDouble&, double); // 2
SmallInt si;
LongDouble ld;
ld = si + ld;
ld = ld + si;
ld = si + ld;具有二义性,调用1需将si转换为LongDouble,ld转换为SmallInt。
调用2需要将si转换为LongDouble,ld转换为double。
ld = ld + si;精确匹配LongDouble operator+ (const SmallInt&);
若调用LongDouble operator+(LongDouble&, double);还需将si转换为double。
14.53 假设我们已经定义了如第522页所示的SmallInt,判断下面的加法表达式是否合法。如果合法,使用了哪个加法运算符?如果不合法,应该怎样修改代码才能使其合法?
SmallInt s1;
double d = s1 + 3.14;
内置的operator+(int, double)是可行的,而3.14可以转换为int,然后再转换为SmallInt,所以SmallInt的成员operator+也是可行的。两者都需要进行类型转换,所以会产生二义性。改为:
double d = s1 +SmallInt(3.14);即可。