本书为Primer C++ 中文第五版
练习题笔记
(14.1)
没有区别的情况:运算符作用于类型正确的实参,如data1 + data2; operator+(data1, data2);
有区别的情况:运算符作用于错误的实参,使用会产生error
(14.2)
class Sales_data;
istream& operator>>(istream &is, Sales_data& sd);
ostream& operator<<(ostream &os, const Sales_data& sd);
class Sales_data {
private:
string isBn;
double price;
public:
Sales_data() = default;
Sales_data(double p, string ib): isBn(ib), price(p) { }
friend istream& operator>>(istream &is, Sales_data& sd);
friend ostream& operator<<(ostream &os, const Sales_data& sd);
Sales_data& operator+(const Sales_data& sd) {
if(isBn == sd.isBn) {
price += sd.price;
}
return *this;
}
Sales_data& operator+=(const Sales_data& sd) {
if(isBn == sd.isBn)
price += sd.price;
return *this;
}
};
istream& operator>>(istream &is, Sales_data& sd) {
is >> sd.isBn >> sd.price;
return is;
}
ostream& operator<<(ostream &os, const Sales_data& sd) {
os << sd.isBn << " " << sd.price << endl;
return os;
}
void test2() {
Sales_data sd0(2.58, "zlbook");
Sales_data sd1(3.96, "zlbook");
Sales_data sd2(4.25, "zlbook");
Sales_data sd3(1.47, "zl");
Sales_data sd4;
cout << sd0;
cin >> sd4;
cout << "ad2+sd0" << endl;
sd2 + sd0;
cout << sd0;
cout << sd2;
cout << "ad3+=sd0" << endl;
sd3 += sd0;
cout << sd0;
cout << sd3;
}
(14.3)
(a) const char *
(b) string
(c) vector
(d) string
(14.4)
判定标准:
=, [], (), ->必须是成员
复合赋值符一般来说应该是成员,但并非必须
改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减和解引用,通常应该是成员
具有对称性的运算符可能转换任意一端的运算对象,如算术、相等性、关系和位运算符等,通常应该是非成员
通常不应该重载逗号、取地址、逻辑与和逻辑或运算符
类的成员: %=, ++, ->, ()
非类的成员: %, <<, ==
不应该被重载: &&
(14.5)
class Date {
public:
Date(int y, int m, int d, string ins)
: year(y), month(m), day(d), instructions(ins) { }
private:
int year, month, day;
string instructions;
};
这个类记录了某天的日期和当天的记录
应该需要重载 复合赋值符、相等性、关系、>>和<<运算符
(14.6)
ostream& operator<<(ostream &os, const Sales_data& sd) {
os << sd.isBn << " " << sd.price;
return os;
}
(14.7)
ostream& operator<<(ostream &os, const String& s) {
os << s.p << s.sz;
return os;
}
(14.8)
ostream& operator<<(ostream &os, const Date& d) {
os << d.year << "年" << d.month << "月" << d.day << "日" << " :" << d.instructions;
return os;
}
(14.9)
当读取操作发生错误时,输入运算符应该负责从错误中恢复
istream& operator>>(istream &is, Sales_data& sd) {
is >> sd.isBn >> sd.price;
if(!is) item = Sales_data();//输入失败,对象赋予默认的状态
return is;
}
(14.10)
(a) 正常
(b) 输入失败,对象将被默认初始化
(14.11)
没有处理输入失败的情况,输入对象参数正常时则正常,不正确时将发生错误
(14.12)
istream& operator>>(istream &is, Date& d) {
is >> d.year >> d.month << d.day << d.instructions;
if(!is) d = Date();//输入失败,对象赋予默认的状态
return is;
}
(14.13)
非类成员函数
ostream& operator<<(ostream &os, const String& s);
istream& operator>>(istream &is, Sales_data& sd);
Sales_data operator+(const Sales_data& lhs, const Sales_data& rhs);
类成员函数
Sales_data& Sales_data::operator+=(const Sales_data& rhs);
(14.14)
+=返回的是一个引用,可以占用更少的内存
(14.15)
不需要,因为日期相加没有意义
(14.16)
就实现其中一个StrBlob,其他的都大同小异,都定义成非成员函数
bool operator==(const StrBlob& lsb, const StrBlob& rsb);
bool operator!=(const StrBlob& lsb, const StrBlob& rsb);
(14.17)
需要
bool operator==(const Date& ld, const Date& rd) {
return ld.year == rd.year &&
ld.month == rd.month &&
ld.day == rd.day &&
ld.instructions == rd.instructions;
}
bool operator!=(const Date& ld, const Date& rd) {
return !(ld == rd);
}
(14.18)
就实现其中一个StrBlob,其他的都大同小异,都定义成非成员函数
bool operator<(const StrBlob& lsb, const StrBlob& rsb);
bool operator>(const StrBlob& lsb, const StrBlob& rsb);
(14.19)
应该含有
bool operator<(const Date& ld, const Date& rd) {
if(ld.year < rd.year) return true;
else if(ld. year > rd.year) return false;
else {
if(ld.month < rd.month) return true;
else if(ld.month > rd.month) return false;
else {
if(ld.day < rd.day) return true;
else if(ld.day > rd.day) return false;
else {
if(ld.instructions < rd.instructions) return true;
else return false;
}
}
}
}
bool operator>(const Date& ld, const Date& rd) {
return !(ld < rd) && (ld != rd);
}
(14.20)
非类成员函数
Sales_data operator+(const Sales_data& lhs, const Sales_data& rhs);
类成员函数
Sales_data& Sales_data::operator+=(const Sales_data& rhs);
(14.21)
//+调用+=
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;
}
//+=调用+
Sales_data operator+(const Sales_data& lhs, const Sales_data& rhs) {
Sales_data sum = lhs;
sum.units_sold += rhs.units_sold;
sum.revenue += rhs.revenue;
return sum;
}
Sales_data& Sales_data::operator+=(const Sales_data& rhs) {
return *this + rhs;
}
可读性降低
(14.22)
Sales_data& Sales_data::operator=(const string& s) {
ISBN = s;
return *this;
}
(14.23)
StrVec& StrVec::operator=(initializer_list<string> il) {
auto data = alloc_n_copy(il.begin(), il.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
(14.24)
不需要
(14.25)
不需要
(14.26)
如果一个类包含下标运算符,通常会定义两个版本:
一个返回普通引用,一个是类的常量成员并且返回常量引用
class StrVec {
public:
string& operator[](size_t n) {return elements[n]; }
const string& operator[](size_t n) const {
return elements[n];
}
private:
string* elements;
};
(14.27)
class StrBlobPtr {
public:
//前置版本
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 operator++(int) {
StrBlobPtr ret = *this;
++*this;
return ret;
}
StrBlobPtr operator--(int) {
StrBlobPtr ret = *this;
--*this;
return ret;
}
};
(14.28)
StrBlobPtr operator+(int n) {
StrBlobPtr ret = *this;
ret.curr += n;
check(ret.curr, "increment error");
return ret;
}
StrBlobPtr operator-(int n) {
StrBlobPtr ret = *this;
ret.curr -= n;
check(ret.curr, "decrement error");
return ret;
}
(14.29)
因为递增和递减会改变当前对象值
(14.30)
class ConstStrBlobPtr{
public:
const string& operator*() const {
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
const string* operator->() const {
return & this->operator*();
}
};
(14.31)
因为StrBlobPtr类的成员有拷贝构造函数、赋值运算符及析构函数,不需要再次声明定义。
(14.32)
class Test {
public:
string* operator->() const {
return sbp->operator->();
}
private:
StrBlobPtr *sbp;
};
(14.33)
一个类可以定义多个不同版本的调用运算符,其参数数量可以为0或任意正整数
(14.34)
template <typename T>
class ite {
public:
T operator()(T t1, T t2, T t3) const {
return t1 ? t2 : t3;
}
};
void test34() {
ite <int>i;
ite <double>d;
cout << i(0, 2, 3) << endl;
cout << d(1.2, 2.3, 3.4) << endl;
}
(14.35)
class PrintString {
public:
PrintString(istream &i): is(i) { }
string operator()()const{
string line;
if(!getline(is, line)) line = "";
return line;
}
private:
istream& is;
};
void test35() {
PrintString ps(cin);
cout << ps() << endl;
}
(14.36)
void test36() {
PrintString ps(cin);
vector<string> vs;
while(1) {
string line = ps();
if(!line.empty())
vs.push_back(line);
else break;
}
for(auto s : vs) cout << s << " ";
cout << endl;
}
(14.37)
template <typename T>
class equality {
public:
bool operator()(T a, T b) const {
return a == b;
}
};
void test37() {
vector<int> vi = {0, 1, 2, 3, 4, 2, 3, 2, 3};
equality<int> ei;
for_each(vi.begin(), vi.end(), [ei](int& i){if(ei(2, i)) i = 100;});
for(auto i : vi) cout << i << " ";
cout << endl;
}
(14.38)
class SizeComp{
public:
SizeComp(size_t n): sz(n) { }
bool operator()(const string& s) const {
return s.size() == sz;
}
private:
size_t sz;
};
void test38() {
ifstream fin("text.txt");
string line;
vector<string> vs;
while(getline(fin, line) && !fin.eof()) {
istringstream ss(line);
string word;
while(ss >> word) {
vs.push_back(word);
}
}
for(int i = 1; i < 11; ++i) {
cout << i << ": " << count_if(vs.begin(), vs.end(), SizeComp(i)) << endl;
}
}
(14.39)
void test39() {
ifstream fin("text.txt");
string line;
vector<string> vs;
while(getline(fin, line) && !fin.eof()) {
istringstream ss(line);
string word;
while(ss >> word) {
vs.push_back(word);
}
}
cout << "1-9: " << count_if(vs.begin(), vs.end(), [](const string& s){return s.size()>0&&s.size()<10;}) << endl;
cout << ">=10: " << count_if(vs.begin(), vs.end(), [](const string& s) {return s.size() >= 10;}) << endl;
}
(14.40)
class BigOpe {
public:
bool operator()(const string& s1, const string& s2) const {
return s1.size() < s2.size();
}
bool operator()(const string& s) {
return s.size() >= 5;
}
};
class PrintStr {
public:
void operator()(const string& s) {
cout << s << " ";
}
};
void biggies(vector<string> &words, vector<string>::size_type sz) {
sort(words.begin(), words.end());
auto end_unique = unique(words.begin(), words.end());
words.erase(end_unique, words.end());
stable_sort(words.begin(), words.end(),
[](const string &s1, const string &s2){return s1.size() < s2.size();});
auto wc = find_if(words.begin(), words.end(),
[sz](string &s){return s.size() >= sz;});
auto count = words.end() - wc;
cout << count << " "<< (count > 1 ? "words" : "word") << " of length "
<< sz << " or longger" << endl;
for_each(wc, words.end(), [](string &s){ cout << s << " ";});
cout << endl;
}
void test40() {
vector<string> sve = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red", "turtle"};
cout << "lambda" << endl;
biggies(sve, 5);
cout << "class" << endl;
sort(sve.begin(), sve.end());
auto end_unique = unique(sve.begin(), sve.end());
sve.erase(end_unique, sve.end());
stable_sort(sve.begin(), sve.end(), BigOpe());
auto wc = find_if(sve.begin(), sve.end(), BigOpe());
auto count = sve.end() - wc;
cout << count << " "<< (count > 1 ? "words" : "word") << " of length 5"
<< " or longger" << endl;
for_each(wc, sve.end(), PrintStr());
cout << endl;
}
(14.41)
如果是简单的判断逻辑的话,更愿意使用lambda,感觉使用类有点大材小用了
/**
*template<class Operation, class T>
*binder2nd<Operation> bind2nd (const Operation& op, const T& x) {
* return binder2nd<Operation>(op, typename Operation::second_argument_type(x));
*}
*template<class Operation, class T>
*binder1st<Operation> bind1st (const Operation& op, const T& x) {
* return binder1st<Operation>(op, typename Operation::first_argument_type(x));
*}
*bind2nd 表示 op(const T& t, x)
*bind1st 表示 op(x, const T& t)
*x为调用的时候里面的值,t为该函数捕捉到的值
*/
(14.42)
void test42() {
vector<int> vi = {0, 1, 1038, 2022, 5, 1111, 100};
vector<string> vs = {"pooh", "pooh", "test", "pooh"};
auto sum = count_if(vi.begin(), vi.end(), bind2nd(greater<int>(), 1024));
cout << sum << endl;
auto first = find_if(vs.begin(), vs.end(), bind2nd(not_equal_to<string>(), "pooh"));
cout << *first << endl;
transform(vi.begin(), vi.end(), vi.begin(), bind2nd(multiplies<int>(), 2));
for(auto i : vi) cout << i << " ";
cout << endl;
}
(14.43)
void test43() {
modulus<int> mo;
cout << 3%9<< " " << 9%3 << endl;
cout << mo(3, 9)<< " " << mo(9, 3) << endl;
vector<int> vi = {3, 6, 9, 12 ,15 ,18 ,21};
for(auto i : vi) cout << i << " ";
cout << endl << "please input a number :\n";
int num;
while(cin >> num && num != 0) {
int res = count_if(vi.begin(), vi.end(), bind2nd(modulus<int>(), num));
//cout << res << endl;
if(res == 0) {
cout << "could" << endl;
}else {
cout << "couldn't" << 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 i, int j) {
return i / j;
}
};
void test44() {
map<string, function<int(int, int)>> binops = {
{"+", add},
{"-", minus<int>()},
{"/", divide()},
{"%", mod},
{"*", [](int i, int j) { return i * j;} }
};
cout << "10+2=: " << binops["+"](10,2) << endl;
cout << "10-2=: " << binops["-"](10,2) << endl;
cout << "10/2=: " << binops["/"](10,2) << endl;
cout << "10%2=: " << binops["%"](10,2) << endl;
cout << "10*2=: " << binops["*"](10,2) << endl;
}
(14.45)
string 应该返回 bookID
double 应该返回 revenue
(14.46)
不应该,因为Sales_data中的成员函数都不是唯一属性,三个成员函数结合在一起组成Sales_data;
如果定义了需要被声明成explicit,防止隐式转换产生错误。
(14.47)
operator const int(); 表示将对象定义成const int类型并返回
operator int() const; 表示改函数是const的,返回的是int类型
(14.48)
可以为Date提供一个bool类型的转换云算符,检查日期是否有效;
bool的类型转换通常用在条件部分,因此operator bool一般定义成explicit的
(14.49)
class Date {
public:
Date(int y, int m, int d): year(y), month(m), day(d) { }
explicit operator bool() const {
if((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
return month>0 && month<12 && day>0 && day <= month0[month] && year > 0;
else
return month>0 && month<12 && day>0 && day <= month1[month] && year > 0;
}
private:
int year, month, day;
int month0[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
int month1[12] = {31,29,31,30,31,30,31,31,30,31,30,31};
};
void test49() {
Date b(1997,7,24);
cout << static_cast<bool>(b) << endl;
Date b1(1997,-7,24);
cout << static_cast<bool>(b1) << endl;
}
(14.50)
ex1是int类型,所以ldObj需要转换成int,但是ldObj没有直接转换的,产生二义性
ex2是float类型,而ldObj有转换float类型的转换,调用operator float()转换
(14.51)
会优先调用标准类函数
(14.52)
ld = si + ld;
因为SmallInt和LongDouble不能互相转换,si会转换成int,LongDouble会转换成float或double,产生二义性;
ld = ld + si;
SmallInt和LongDouble不能互相转换,但是LongDouble中operator+接受为SmallInt类型的参数
所以会调用LongDouble的operator+
(14.53)
不合法,候选操作:
operator+(int, double)
SmallInt operator+(const SmallInt&, const SmallInt&)
应该改为:
double d = static_cast<double>(s1) + 3.14;