7.1、7.2、7.3
#include<iostream> #include<cstdio> #include<vector> #include<string> using namespace std; struct Sales_data { string bookNo;//isbn编号 unsigned units_sold=0;//该书的销量 double revenue=0.0;//该书的总销售收入 string isbn()const {return bookNo;} Sales_data& combine(const Sales_data &rhs) { units_sold+=rhs.units_sold; revenue+=rhs.revenue; return *this; } double avg_price() const { if(units_sold) return revenue/units_sold; else return 0; } }; Sales_data add(const Sales_data& lhs,const Sales_data& rhs) { Sales_data sum=lhs; sum.combine(rhs); return sum; } istream &read(istream &is,Sales_data &item) { double price=0; is>>item.bookNo>>item.units_sold>>price; item.revenue=item.units_sold*price; return is; } ostream &print(ostream &os,const Sales_data &item) { os<<item.isbn()<<" "<<item.units_sold<<" "<<item.revenue<<" "<<item.avg_price(); return os; } int main() { Sales_data total; if(read(cin,total)) { Sales_data trans; while(read(cin,trans)) { if(total.isbn()==trans.isbn()) total.combine(trans); else { print(cout,total)<<endl; total=trans; } } print(cout,total)<<endl; } else cerr<<"No data?"<<endl; return 0; }
7.4
struct Person { string name,address; };
7.5
当然应该是const。首先这些成员函数并不修改成员变量,其次为了方便常量调用,所以可以设为const。
struct Person { string name,address; string getName()const { return name; } string getAddress() const { return address; } };
7.6、7.7略
7.8
因为read需要写到该变量中,需要修改该对象的值。print函数不需要改值。
7.9
struct Person { string name,address; string getName()const { return name; } string getAddress() const { return address; } }; istream &read(istream &is,Person &per) { is>>per.name>>per.address; return is; } ostream &print(ostream &os,const Person &per) { os<<per.getName()<<" "<<per.getAddress(); return os; } int main() { Person p; while(read(cin,p)) print(cout,p)<<endl; return 0; }
7.10
成功读取到数据
7.11
#include<iostream> #include<cstdio> #include<vector> #include<string> using namespace std; struct Sales_data { string bookNo;//isbn编号 unsigned units_sold=0;//该书的销量 double revenue=0.0;//该书的总销售收入 Sales_data(const string &s):bookNo(s),units_sold(0),revenue(0){} string isbn()const {return bookNo;} Sales_data& combine(const Sales_data &rhs) { units_sold+=rhs.units_sold; revenue+=rhs.revenue; return *this; } double avg_price() const { if(units_sold) return revenue/units_sold; else return 0; } }; Sales_data add(const Sales_data& lhs,const Sales_data& rhs) { Sales_data sum=lhs; sum.combine(rhs); return sum; } istream &read(istream &is,Sales_data &item) { double price=0; is>>item.bookNo>>item.units_sold>>price; item.revenue=item.units_sold*price; return is; } ostream &print(ostream &os,const Sales_data &item) { os<<item.isbn()<<" "<<item.units_sold<<" "<<item.revenue<<" "<<item.avg_price(); return os; } int main() { string s; cin>>s; Sales_data total(s); print(cout,total)<<endl; return 0; }
7.12
这里由于是在类内所以需要提前声明函数,而函数里又有该类,所以需要提前声明该类。
struct Sales_data; istream &read(istream&,Sales_data&); struct Sales_data { string bookNo;//isbn编号 unsigned units_sold=0;//该书的销量 double revenue=0.0;//该书的总销售收入 Sales_data(const string &s):bookNo(s),units_sold(0),revenue(0){} Sales_data(istream &is) { read(is,*this); } string isbn()const {return bookNo;} Sales_data& combine(const Sales_data &rhs) { units_sold+=rhs.units_sold; revenue+=rhs.revenue; return *this; } double avg_price() const { if(units_sold) return revenue/units_sold; else return 0; } }; Sales_data add(const Sales_data& lhs,const Sales_data& rhs) { Sales_data sum=lhs; sum.combine(rhs); return sum; } istream &read(istream &is,Sales_data &item) { double price=0; is>>item.bookNo>>item.units_sold>>price; item.revenue=item.units_sold*price; return is; } ostream &print(ostream &os,const Sales_data &item) { os<<item.isbn()<<" "<<item.units_sold<<" "<<item.revenue<<" "<<item.avg_price(); return os; } int main() { Sales_data total(cin); print(cout,total)<<endl; return 0; }
7.13
略
7.14
如果没有初始化某个成员,那么它将用类内初始值初始化。
#include<iostream> #include<cstdio> #include<vector> #include<string> using namespace std; struct Sales_data; istream &read(istream&,Sales_data&); struct Sales_data { string bookNo;//isbn编号 unsigned units_sold=50;//该书的销量 double revenue=10.0;//该书的总销售收入 Sales_data() {} Sales_data(const string &s):bookNo(s),units_sold(0),revenue(0) {} Sales_data(istream &is) { read(is,*this); } string isbn()const { return bookNo; } Sales_data& combine(const Sales_data &rhs) { units_sold+=rhs.units_sold; revenue+=rhs.revenue; return *this; } double avg_price() const { if(units_sold) return revenue/units_sold; else return 0; } }; Sales_data add(const Sales_data& lhs,const Sales_data& rhs) { Sales_data sum=lhs; sum.combine(rhs); return sum; } istream &read(istream &is,Sales_data &item) { double price=0; is>>item.bookNo>>item.units_sold>>price; item.revenue=item.units_sold*price; return is; } ostream &print(ostream &os,const Sales_data &item) { os<<item.isbn()<<" "<<item.units_sold<<" "<<item.revenue<<" "<<item.avg_price(); return os; } int main() { Sales_data total; print(cout,total)<<endl; return 0; }
7.15
struct Person { string name,address; Person(string &_name,string &_address):name(_name),address(_address){} string getName()const { return name; } string getAddress() const { return address; } };
7.16
在类外允许访问的成员应该在public后面
只允许在类内访问的成员应该在private后面
7.17
struct默认访问权限和继承方式是public
class默认访问权限和继承方式是private
7.18
封装:隐藏对象的属性和实现细节,仅对外开放接口,控制程序中的属性读取和修改的访问级别。
增强安全性和简化编程。
7.19
成员变量name、address应该声明为private,构造函数,getName,getAddress应该声明为public。
7.20
有些类或者函数需要访问该类的非公有成员,可以声明为友元。
好处是实现了访问类内非公有成员
坏处破坏了封装性
7.21
#include<iostream> #include<cstdio> #include<vector> #include<string> using namespace std; class Sales_data; istream &read(istream&,Sales_data&); ostream &print(ostream&,const Sales_data&); class Sales_data { friend istream &read(istream&,Sales_data&); friend ostream &print(ostream&,const Sales_data&); //friend Sales_data add(const Sales_data&,const Sales_data&); private: string bookNo;//isbn编号 unsigned units_sold=50;//该书的销量 double revenue=10.0;//该书的总销售收入 public: Sales_data() {} Sales_data(const string &s):bookNo(s),units_sold(0),revenue(0) {} Sales_data(istream &is) { read(is,*this); } string isbn()const { return bookNo; } Sales_data& combine(const Sales_data &rhs) { units_sold+=rhs.units_sold; revenue+=rhs.revenue; return *this; } double avg_price() const { if(units_sold) return revenue/units_sold; else return 0; } }; Sales_data add(const Sales_data& lhs,const Sales_data& rhs) { Sales_data sum=lhs; sum.combine(rhs); return sum; } istream &read(istream &is,Sales_data &item) { double price=0; is>>item.bookNo>>item.units_sold>>price; item.revenue=item.units_sold*price; return is; } ostream &print(ostream &os,const Sales_data &item) { os<<item.isbn()<<" "<<item.units_sold<<" "<<item.revenue<<" "<<item.avg_price(); return os; } int main() { Sales_data total; print(cout,total)<<endl; return 0; }
7.22
class Person { private: string name,address; public: Person(string &_name,string &_address):name(_name),address(_address) {} string getName()const { return name; } string getAddress() const { return address; } };
7.23
#include<iostream> #include<cstdio> #include<vector> #include<string> using namespace std; class Screen { public: typedef std::string::size_type pos; Screen()=default; Screen(pos ht,pos wd,char c):height(ht),width(wd),contents(ht*wd,c){} char get() const { return contents[cursor]; } inline char get(pos ht,pos wd) const; Screen &move(pos r,pos c); private: pos cursor=0; pos height=0,width=0; string contents; }; inline Screen &Screen::move(pos r,pos c) { pos row=r*width; cursor=row+c; return *this; } char Screen::get(pos r,pos c) const { return contents[r*width+c]; }
7.24
Screen()=default; Screen(pos ht,pos wd):height(ht),width(wd),contents(ht*wd,' '){} Screen(pos ht,pos wd,char c):height(ht),width(wd),contents(ht*wd,c){}
7.25
能。没有涉及一些指针类的操作。
7.26
inline double avg_price() const { if(units_sold) return revenue/units_sold; else return 0; }
7.27
using namespace std; class Screen { public: typedef std::string::size_type pos; Screen()=default; Screen(pos ht,pos wd):height(ht),width(wd),contents(ht*wd,' ') {} Screen(pos ht,pos wd,char c):height(ht),width(wd),contents(ht*wd,c) {} char get() const { return contents[cursor]; } inline char get(pos ht,pos wd) const; Screen &move(pos r,pos c); Screen &set(char c) { contents[cursor]=c; return *this; } Screen& display(ostream &os) { os<<height<<" "<<width<<" "<<contents; return *this; } private: pos cursor=0; pos height=0,width=0; string contents; }; inline Screen &Screen::move(pos r,pos c) { pos row=r*width; cursor=row+c; return *this; } char Screen::get(pos r,pos c) const { return contents[r*width+c]; } int main() { Screen myScreen(5,5,'X'); myScreen.move(4,0).set('#').display(cout); cout<<endl; myScreen.display(cout); cout<<endl; return 0; }
输出:
5 5 XXXXXXXXXXXXXXXXXXXX#XXXX 5 5 XXXXXXXXXXXXXXXXXXXX#XXXX
7.28
修改之后的输出:
5 5 XXXXXXXXXXXXXXXXXXXX#XXXX 5 5 XXXXXXXXXXXXXXXXXXXXXXXXX
第一次输出时候的仅仅是原对象的一个副本,不是原来的对象。
所以第二次输出原来的对象,没有改变过。
7.29
略
7.30
如果有局部变量与成员变量同名会隐藏了该成员变量,所以可以通过this指针显式使用,其他情况下用this与不用都是一样的。
7.31
using namespace std; class X; class Y; class X { Y *py; }; class Y { X x; };
声明之后,定义之前,该类是一种不完全类型。不完全类型只能定义指针或引用。
7.32
这个题有点难。
难在程序代码的组织。
第一步声明Screen。因为下面在定义Windows_mgr中,需要用到Screen类,注意没有用到Screen的成员。
第二步定义Windows_mgr同时声明clear但是不能定义clear,因为clear用到了Screen的成员,而此时Screen还未定义。
第三步定义Screen包括对于clear的友元声明,此时clear的声明已经存在
第四步定义clear,此时它才可以使用Screen的成员。
using namespace std; class Screen; class Window_mgr { public: using ScreenIndex=std::vector<Screen>::size_type; void clear(ScreenIndex); private: std::vector<Screen> screens ; }; class Screen { // friend class Window_mgr; friend void Window_mgr::clear(ScreenIndex ); public: typedef std::string::size_type pos; Screen()=default; Screen(pos ht,pos wd):height(ht),width(wd),contents(ht*wd,' ') {} Screen(pos ht,pos wd,char c):height(ht),width(wd),contents(ht*wd,c) {} char get() const { return contents[cursor]; } inline char get(pos ht,pos wd) const; Screen &move(pos r,pos c); Screen &set(char c) { contents[cursor]=c; return *this; } Screen& display(ostream &os) { os<<height<<" "<<width<<" "<<contents; return *this; } private: pos cursor=0; pos height=0,width=0; string contents; }; inline Screen &Screen::move(pos r,pos c) { pos row=r*width; cursor=row+c; return *this; } char Screen::get(pos r,pos c) const { return contents[r*width+c]; } void Window_mgr::clear(ScreenIndex i) { Screen &s=screens[i]; s.contents=string(s.height*s.width,' '); }
以前写代码都是声明的同时定义,感觉声明了以后隔好远才定义都是自己找麻烦。。这次算是有了新的体会。有时候必须提前声明但是无法定义,必须按一定顺序组织代码。
一个非常重要的地方,声明一个类,在后面定义之前只能用这个类名,而不能使用它的成员,因为编译器不知道它的成员都有谁。
7.33
遇到一个编译器无法识别pos,因为pos它不在类的作用域内。
正确写法:
Screen::pos Screen::size() const { return height*width; }
7.34
编译错误,pos未定义。
7-35
编译错误。
首先一个错误是在定义setVal的地方,返回值这里的Type是全局的,即string,与类内声明不符,因此会编译错误。
正确写法:Exercise::Type Exercise::setVal(Type parm)
这样返回值就是double。
但是这个代码还是不能通过编译,因为initVal没有定义。由于在函数体内已经进入了类的作用域,所以这里调用的是类的initVal。
下面是我修改以后的代码。
#include<iostream> #include<cstdio> #include<vector> #include<string> using namespace std; typedef int Type; Type initVal() { cout<<"use func!"<<endl; } class Exercise { public: typedef double Type; Type setVal(Type); Type initVal() { cout<<"use class!"<<endl; } private: int val; }; Exercise::Type Exercise::setVal(Type parm) { //val=parm+::initVal();//这样写就可以调用全局的initVal,注意我把全局的Type换成了int,因为string无法与double相加 val=parm+initVal();//这样写就可以调用类的initVal return val; } int main() { Exercise e; e.setVal(1.5); return 0; }
7.36
struct X { X(int i,int j):base(i),rem(base%j) {} int rem,base; }; int main() { X x(5,4); cout<<x.base<<" "<<x.rem<<endl; return 0; }
成员变量初始化的顺序是与定义时的顺序一致的,因此上面先初始化rem(此时base是未定义的)再初始化base。所以最后结果是违背设计初衷的。
7.37
两个都调用了Sales_data(string s=""):bookNo(s){}
第一个bookNo="",units_sold=0,revenue=0.0.第一个是默认实参初始化,后两个是类内初始值
第二个bookNo="9-999-99999-9",unit_sold=0,revenue=0.0,第一个是直接初始化,后两个是类内初始值
7.38
Sales_data(istream &is=cin) { read(is,*this); }
但此时不能在显式定义无参构造函数,因为两者会产生二义性调用。
7.39
不合法。二义性调用。
7.40
略
7.41
#include<iostream> #include<cstdio> #include<vector> #include<string> using namespace std; class Sales_data; istream &read(istream&,Sales_data&); ostream &print(ostream&,const Sales_data&); class Sales_data { friend istream &read(istream&,Sales_data&); friend ostream &print(ostream&,const Sales_data&); //friend Sales_data add(const Sales_data&,const Sales_data&); private: string bookNo;//isbn编号 unsigned units_sold=50;//该书的销量 double revenue=10.0;//该书的总销售收入 public: Sales_data():Sales_data("",0,0) { cout<<"我是无参构造函数!"<<endl; } Sales_data(const string &s,unsigned cnt,double price):bookNo(s),units_sold(cnt),revenue(cnt*price) { cout<<"我是三参构造函数!"<<endl; } Sales_data(const string &s):Sales_data(s,0,0) { cout<<"我是一参构造函数!"<<endl; } Sales_data(istream &is):Sales_data() { cout<<"我是istream构造函数!"<<endl; read(is,*this); } string isbn()const { return bookNo; } Sales_data& combine(const Sales_data &rhs) { units_sold+=rhs.units_sold; revenue+=rhs.revenue; return *this; } double avg_price() const { if(units_sold) return revenue/units_sold; else return 0; } }; Sales_data add(const Sales_data& lhs,const Sales_data& rhs) { Sales_data sum=lhs; sum.combine(rhs); return sum; } istream &read(istream &is,Sales_data &item) { double price=0; is>>item.bookNo>>item.units_sold>>price; item.revenue=item.units_sold*price; return is; } ostream &print(ostream &os,const Sales_data &item) { os<<item.isbn()<<" "<<item.units_sold<<" "<<item.revenue<<" "<<item.avg_price(); return os; } int main() { Sales_data a; Sales_data b("123"); Sales_data c(cin); return 0; }
先执行委托的构造函数。
7.42
略
7.43
以下代码是不能通过编译的。因为C的默认构造函数会调用一个无参的Nodefault的构造函数,而它是不存在的。
using namespace std; class Nodefault { public: Nodefault(int x) {} }; class C { private: Nodefault nd; public: C()=default; }; int main() { C c; return 0; }
7.44
编译错误,缺少Nodefault的无参构造函数。
7.45
不合法,缺少Nodefault的无参构造函数。
7.46
(a)是的
(b)不是。也可能是有参数但是还有默认实参的构造函数。
(c)不对。默认构造函数是编译器自己合成的。即使该类没有成员变量,它依旧会被提供。
(d)是的。
7.47
可以吧?
7.48
如果只有Sales_data(const string &s);这一个函数的话,似乎不受影响。
7.49
(a)string隐式转化成Sales_data,要求该构造函数Sales_data(const string &s)不是explicit的
(b)无法通过编译。不懂?因为临时变量不能被绑定到普通引用上?
(c)string隐式转化成Sales_data&,要求该构造函数Sales_data(const string &s)不是explicit的
7.50
似乎都可以
7.51
string s="sfds"这样是合法的
但是vector<int> vec=10;这样缺少违背了设计初衷的。
7.52
这样初始化方法只有聚合类才支持。
而64页的该类由于提供了类内初始值因此不是聚合类。
正确写法
using namespace std; struct Sales_data { string bookNo;//isbn编号 unsigned units_sold;//该书的销量 double revenue;//该书的总销售收入 }; int main() { Sales_data total={"ABCD",12,34.567}; return 0; }
7.53
略
7.54
不能。constexpr 的成员函数是隐身const,不能修改成员变量。
7.55
没有constexpr 构造函数?
7.56
所有类的对象共享的成员称为类的静态成员。
普通成员,类的每个对象独占一份。
7.57
略
7.58
不能在类内初始化静态成员,const静态成员除外。
正确写法
class Example { public: static double rate; static const int vecSize=20; static vector<double> vec; }; double Example::rate=10; vector<double> Example::vec(vecSize);