c++ primer(第五版)学习笔记及习题答案代码版(第七章)类

来源:互联网 发布:java 中文文件名乱码 编辑:程序博客网 时间:2024/04/29 05:26
笔记较为零散,都是自己不熟悉的知识点。

习题答案至于一个.cc 中,需要演示某一题直接修改 #define NUM***, 如运行7.23题为#define NUM723;

chapter 7
1、
定义在类内部的函数时隐式的inline函数
成员函数通过一个名为this的额外的参数来访问调用它的那个对象,当我们调用一个成员函数时,用请求该函数的对象地址初始化this,比如调用total.isbn()时,
编译器负责把total的地址传递给isbn的隐式形参this,可以等价认为编译重写为:
Sales_data::ibsn(&total),
其中,调用Sales_data的isbn成员时传入;额total的地址。
this总是执行啊这个对象,所以this是一个常量指针。
this设置为指向常量的指针有助于提高函数的灵活性。
紧跟在参数列表之后的const表示this是一个指向常量的指针。这一函数称为常量成员函数。
2、
一般来说,执行输出任务的函数应该尽量减少对格式的控制,这样可以确保由用户代码来决定是够换行。
不同的构造函数之间必须在参数数量或参数类型上有所区别。
构造函数不能被声明成const的。
默认的构造函数无需任何实参,编译器创建的构造函数称为合成的默认构造函数:
  如果存在类内的初始值,用其来初始化成员。否则,默认初始化该成员。
如果在类外定义构造函数,因为其没有返回值,必须指明是哪个类的成员。
使用this将对象作为一个整体访问,而非直接访问对象的某个成员。
使用*this将this对象作为实参传递给成员函数。
3、
当希望定义的类所有成员时public时,使用struct;反之,如希望成员时private,使用class。
友元声明出现在类定义的内部,一般最好在类定义开始或结束前的位置集中声明友元。
友元不是类成员,也不受它所在区域访问级别的约束。
友元的声明仅仅指定了访问的权限,而非通常意义上的函数声明。所以要在友元函数之外另起声明。
4、
当我们提供一个类内初始值时,必须以符号=或花括号表示。
一个const成员函数如果以引用的形式返回*this,那么它的返回类型将是常量引用。
类名用来直接指向类类型,也可以将类名跟于class/struct之后:
Sales_data item1;
class Sales_data item1;  //等价的声明
5、
友元关系不存在传递性,每个类负责控制自己的友元函数。
名字查找(name lookup)
现在在名字所在的块中寻找其声明语句,只考虑在名字的使用之前出现的声明。
如果没找到,继续查找外层作用域。
最终没找打,报错。
类的定义分为两步: 首先,编译成员的声明;直到类全部可见后才编译函数体。
6、
类型名的定义通常出现在类的开始处,这样能确保所有使用该类型的成员都出现在类名的定义之后。一般不建议使用其他成员的名字作为某个成员函数的参数。
成员是const、引用,或者属于某种未提供默认构造函数的类类型,必须通过构造函数初始值为这些成员提供初值。
构造函数初始值的顺序与成员声明的顺序保持一致。
7、
如果一个构造函数为所有参数都提供了默认实参,则它实际上也定义了默认的构造函数。
实际中,如果定义了其他构造函数,那么最好也提供一个默认的构造函数。
8、
静态成员函数不能与任何对象绑定在一起,它们不包含this指针。静态成员函数也不能声明成const的,并且也不能在static函数体内使用this指针。
非静态数据成员不能作为默认实参,因为它的值本身属于对象的一部分,如果这门做就无法真正提供一个对象以便从中获取成员的值,引发错误。

#ifndef CHAPTER7_H#define CHAPTER7_H//Chapter7.h#include <iostream>#include <vector>using namespace std;class Sales_data;istream &read(istream &is, Sales_data &item);class Sales_data {friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs);friend istream &read(istream &is, Sales_data &item);friend ostream &print(ostream &os, const Sales_data &item);public:Sales_data(){};Sales_data(const string &s):bookNo(s), units_sold(0), revenue(0.0){ }Sales_data(const string &s, unsigned n, double p): bookNo(s), units_sold(n), revenue(p){ };Sales_data(istream &is){ read(is, *this);}string isbn() const{ return bookNo; }Sales_data& combine(const Sales_data&);private:inline double avg_prive() const;string bookNo;unsigned units_sold;double revenue;};Sales_data& Sales_data::combine(const Sales_data& rhs){units_sold += rhs.units_sold;revenue += rhs.revenue;return *this;}double Sales_data::avg_prive() const{return units_sold ? revenue / units_sold : 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.0);is >> item.bookNo >> item.units_sold >> price;item.revenue = price * item.units_sold;return is;}ostream &print(ostream &os, const Sales_data &item){os << item.isbn() << " " << item.units_sold << " " <<item.revenue;return os;}class Person;istream &read(istream &is, Person &item);class Person{friend istream &read(istream &is, Person &item);friend ostream &print(ostream &os, const Person &item);public:Person(){};Person(const string &n, const string &addr): name(n), address(addr){ }Person(istream &is){ read(is, *this); }private:string name;string address;string getName() const { return name; }string getAddress()const  { return address; }};istream &read(istream &is, Person &p){is >> p.name >> p.address;if(!is) p = Person();return is;}ostream &print(ostream &os, const Person &p){os << p.getName() << p.getAddress();return os;}class Screen;class Window_mgr{public:typedef vector<Screen>::size_type ScreenIndex;inline void clear(ScreenIndex);private:vector<Screen> screens;};class Screen{friend void Window_mgr::clear(ScreenIndex);public:typedef string::size_type pos;Screen(){}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(){return contents[cursor]; }char get(pos r, pos c) const { return contents[r * width + c]; }inline Screen &set(char c);inline Screen &set(pos r, pos c, char ch);inline Screen &move(pos r, pos c);const Screen &display(ostream &os) const{os << contents;return *this;}pos size() const;private:pos cursor;pos height, width;string contents;};Screen::pos Screen::size() const{return height * width;}inline Screen &Screen::set(char c){contents[cursor] = c;return *this;}inline Screen &Screen::set(pos r, pos col, char ch){contents[r * width + col] = ch;return *this;}inline Screen &Screen::move(pos r, pos c){pos row = r * width + c;cursor = row + c;return *this;}#endif
//main.cc#include <iostream>#include <vector>#include <cstring>#include "Chapter7.h"using namespace std;#define NUM758/*7.1*/#ifdef NUM71struct Sales_data{string bookNo;unsigned units_sold;double revenue;};#endif/*7.2*/#ifdef NUM72struct Sales_data {string isbn() const{ return bookNo; }Sales_data& combine(const Sales_data&);string bookNo;unsigned units_sold;double revenue;};Sales_data& Sales_data::combine(const Sales_data& rhs){units_sold += rhs.units_sold;revenue += rhs.revenue;return *this;}#endif/*7.4*/#ifdef NUM74class Person{string name;string address;};#endif/*7.5*/#ifdef NUM75class Person{string name;string address;string getName() const { return name; }string getAddress()const  { return address; }};#endif/*7.31*/#ifdef NUM731class Y;class X{Y *p = nullptr;};class Y{X b;};#endif/*7.35*/#ifdef NUM735typedef string Type;Type initVal();  //use 'string' class Exercise{public:typedef double Type; Type setVal(Type);  //use 'double'Type initVal(){   //use 'double'return val;}private:int val;};Exercise::Type Exercise::setVal(Type parm){ //first uses 'string', second uses 'double'val = parm + initVal();          //use 'double'return val;}#endif/*7.58*/#ifdef NUM758class Example{public:static double rate;static const int vecSize = 20;static vector<double> vec;};#endifint main(){/*7.1*/#ifdef NUM71Sales_data total;if(cin >> total.bookNo >> total.units_sold >> total.revenue){Sales_data trans;while(cin >> trans.bookNo >> trans.units_sold >> trans.revenue){if(total.bookNo == trans.bookNo){total.units_sold += trans.units_sold;total.revenue += trans.revenue; }else{cout <<  total.bookNo <<" "<<total.units_sold<< " "<< total.revenue << endl;total = trans;}}cout <<  total.bookNo <<" "<<total.units_sold<< " "<< total.revenue << endl;}else{cerr<< "No data ?"<<endl;return -1;}#endif/*7.2*/#ifdef NUM72cout<<"见main之前类声明. "<<endl;#endif/*7.3*/#ifdef NUM73Sales_data total;if(cin >> total.bookNo >> total.units_sold >> total.revenue){Sales_data trans;while(cin >> trans.bookNo >> trans.units_sold >> trans.revenue){if(total.isbn() == trans.isbn())total.combine(trans);else{cout <<  total.bookNo <<" "<<total.units_sold<< " "<< total.revenue << endl;total = trans;}}cout <<  total.bookNo <<" "<<total.units_sold<< " "<< total.revenue << endl;}else{cerr<< "No data ?"<<endl;return -1;}#endif/*7.4*/#ifdef NUM74cout<<"见main之前类声明. "<<endl;#endif/*7.5*/#ifdef NUM75cout << "需要加const, 表示这是一个指向const类型的指针. "<<endl;#endif/*7.6*/#ifdef NUM76cout<<"见main之前的函数定义. "<<endl;#endif/*7.7*/#ifdef NUM77Sales_data total;if(read(cin, total)){Sales_data trans;while(read(cin, trans)){if(total.isbn() == trans.isbn())total.combine(trans);else{print(cout, trans) << endl;total = trans;}}print(cout, total) << endl;}else{cerr<< "No data ?"<<endl;return -1;}#endif/*7.8*/#ifdef NUM78cout << "read用普通引用,因为它将改变类的成员值,print用常量引用,因为它不会改变对象的成员值. "<<endl;#endif/*7.9*/#ifdef NUM79cout << "见Chapter7.h"<<endl;#endif/*7.10*/#ifdef NUM710cout << "if条件可以同时读取类的两个对象." <<endl;#endif/*7.11*/#ifdef NUM711Sales_data item1; //没有()print(cout, item1) << endl;Sales_data item2("123X");print(cout, item2) << endl;Sales_data item3("123X", 3, 12.00);print(cout, item3) << endl;Sales_data item4(cin);print(cout, item4) << endl;#endif/*7.13*/#ifdef NUM713Sales_data total(cin);if(!total.isbn().empty()){istream &is = cin;while(is){Sales_data trans(is);if(total.isbn() == trans.isbn())total.combine(trans);else{print(cout, total) << endl;total = trans;}}}else{cerr<< "No data ?"<<endl;return -1;}#endif/*7.14*/#ifdef NUM714cout<<"见Chapter7.h构造函数声明. "<<endl;#endif/*7.15*/#ifdef NUM715cout << "见Chapter7.h"<<endl;#endif/*7.16*/#ifdef NUM716cout << "没有限制,对于能够被程序的所有部分访问的成员置于public之public之后,对于那些只希望被成员函数访问的成员置于private之后. " <<endl;#endif/*7.17*/#ifdef NUM717cout <<"基本一样,但是访问权限不同; struct定义在第一个访问说明符之前的成员是public, class关键字在第一个访问说明符之前的是private.  "<<endl;#endif/*7.18*/#ifdef NUM718cout <<"封装实现了接口和具体实现的分离; 好处就是用户层面的代码不能破坏封装对象的状态,实现了封装类的细节隐藏. "<<endl;#endif/*7.19*/#ifdef NUM719cout <<"name/addres被声明为private,getName/getAddress被声明为public,接口需要被定义为公开的,而数据不应该暴露在类之外. "<<endl;#endif/*7.20*/#ifdef NUM720cout <<"在允许其他类访问一个类的非公有成员时. 好处是方便的访问非共有数据,而不用显式的添加前缀来访问。弊处是降低了封装型,造成了代码的冗余. "<<endl;#endif/*7.21*/#ifdef NUM721cout <<"直接在.h中修改,包括添加友元函数声明,添加public/private作用符.运行7.13和7.7中的程序实现. "<<endl;#endif/*7.23*/#ifdef NUM723cout << "见Chapter7.h"<<endl;#endif/*7.24*/#ifdef NUM724cout << "见Chapter7.h"<<endl;#endif/*7.25*/#ifdef NUM725cout <<"能够依赖,聚合版本对于string和vector类型的拷贝赋值有效."<<endl;#endif/*7.26*/#ifdef NUM726cout << "见Chapter7.h, Sales_data类"<<endl;#endif/*7.27*/#ifdef NUM727Screen myScreen(5, 5, 'X');myScreen.move(4, 0).set('#').display(cout);cout << "\n";myScreen.display(cout);cout << "\n";#endif/*7.28*/#ifdef NUM728cout << "有&, 打印:\n""XXXXXXXXXXXXXXXXXXXX#XXXX \n""XXXXXXXXXXXXXXXXXXXX#XXXX""没有&,打印:\n""XXXXXXXXXXXXXXXXXXXX#XXXX \n""XXXXXXXXXXXXXXXXXXXXXXXXX" <<endl;#endif/*7.30*/#ifdef NUM730cout<<"优点:显示的,容易阅读;缺点,有时候会多余,比如可以直接返回成员时用this->"<<endl;#endif/*7.31*/#ifdef NUM731cout<<"见main之前类声明. "<<endl;#endif/*7.32*/#ifdef NUM732cout << "见Chapter7.h"<<endl;#endif/*7.33*/#ifdef NUM733cout << "报错,找不到pos类型,修改在pos前加上Screen. "#endif/*7.34*/#ifdef NUM734cout << "报错pos未定义. "<<endl;#endif/*7.35*/#ifdef NUM735cout << "类外的initval是string类型,类内的Type都是double类型,setVal定义中第一个Type是string类型,第二个是double类型。 修改: Tpye返回类型前加上Exercise, 同时定义initVal成员函数. "<<endl;#endif/*7.36*/#ifdef NUM736cout <<"因为构造函数中base先初始化,rem用base来初始化,但是base的声明在后,所以此时rem初始化参数是未声明的. "<<endl;struct X{X(int i, int j): base(i), rem(base % j) {}int base, rem;};#endif/*7.37*/#ifdef NUM737cout <<"第一句使用Sales_data(std::istream &is);  第二句使用Sales_data(string s = ""): bookNo(""), cnt = 0, revenue = 0.0; 第三句使用Sales_data(std::string s = ""); bookNo = "9-999-99999-9", cnt = 0, revenue = 0.0 "<<endl;#endif/*7.38*/#ifdef NUM738cout << "Sales_data(istream &is = cin){ read(is, *this); } "<<endl;#endif/*7.39*/#ifdef NUM739cout <<"是非法的,Sales_data()构造函数重载,声明冲突."<<endl;#endif/*7.40*/#ifdef NUM740class Book {public:Book() = default;Book(unsigned no, string name, string author, string pubdate):no_(no), name_(name), author_(author), pubdate_(pubdate) { }Book(istream &in) { in >> no_ >> name_ >> author_ >> pubdate_; }private:unsigned no_;string name_;string author_;string pubdate_;};#endif/*7.41*/#ifdef NUM741cout << "1.Default way:"<<endl;Sales_data s1;cout << "2. usng string as parameter: "<<endl;Sales_data s2("c++ & linux");cout << "3. complete parameters: " <<endl;Sales_data s3("c++ & linux", 3, 20.8);cout << "4. using istream as parameter: "<<endl;Sales_data s4(cin);#endif/*7.42*/#ifdef NUM742class Book {public:Book(unsigned no, std::string name, std::string author, std::string pubdate):no_(no), name_(name), author_(author), pubdate_(pubdate) { }Book() : Book(0, "", "", "") { }Book(std::istream &in) : Book() { in >> no_ >> name_ >> author_ >> pubdate_; }private:unsigned no_;std::string name_;std::string author_;std::string pubdate_;};#endif/*7.43*/#ifdef NUM743class NoDefault{public:NoDefault(int){}};class C{public:C(): member(0){} //定义C的构造函数private:NoDefault member;};#endif/*7.44*/#ifdef NUM744cout <<"不合法,应为vector有10个元素,每个都必须被默认初始化。但临时变量显然没有默认初始化. "<<endl;#endif/*7.45*/#ifdef NUM745cout << "可以,因为C定义了默认构造函数. "<<endl;#endif/*7.46*/#ifdef NUM746cout <<"(a)不正确,没有构造函数编译器生成合成默认构造函数.""(b)不正确,默认构造函数是在没有初始值提供的情况下,或者类本身含有类类型成员且使用合成的默认构造函数时.""(c)不正确,都需要提供构造函数.""(d)不正确,只有当类没有显式的定义任何构造函数时,编译器才会生成默认构造函数."#endif/*7.47*/#ifdef NUM747cout <<"可以,防止string被编译器隐式转换成Sales_data对象.优点是防止构造函数发生隐式转换。缺点是只能以直接初始化的形式使用."#endif/*7.48*/#ifdef NUM748cout<<"1.创建了一个临时的string对象,然后调用string的拷贝构造函数,将null_isbn初始化为该临时对象的副本。2.使用string对象null_isbn为实参,调用Sales_data构造函数创建对象null。3.生成string临时对象,调用临时对象作为实参,调用Sales_data构造函数创建对象null. 如果Sales_data的构造函数时显式的,那么第三句将是未定义的。"#endif/*7.49*/#ifdef NUM749cout <<"(a)通过 (b)编译出错,string类型不能转换成Sales_data类型. (c)combine是操作不能定义为const Sales_data类型."<<endl;#endif/*7.50*/#ifdef NUM750cout <<"输入可以声明为显示的,explicit Person(std::istream& is) { read(is, *this); } "<<endl;#endif/*7.51*/#ifdef NUM751cout <<"为符合C风格的编写风格,string用来代替const char*, 所以我们可以使用'linux'转换成string;但是vector不是,当vector&作为形参是不用整形来隐式替换."#endif/*7.52*/#ifdef NUM752cout <<"错误,聚合类内没有初始值。应该将64页中units_sold和revenue的初始值去除."#endif/*7.53*/#ifdef NUM753class Debug{public:    constexpr Debug(bool b = true) : rt(b), io(b), other(b) {}    constexpr Debug(bool r, bool i, bool o) : rt(r), io(i), other(0) {}    constexpr bool any() { return rt || io || other; }    void set_rt(bool b) { rt = b; }    void set_io(bool b) { io = b; }    void set_other(bool b) { other = b; }private:    bool rt;    // runtime error    bool io;    // I/O error    bool other; // the others};#endif/*7.54*/#ifdef NUM754cout <<"不可以,因为constexpr拥有的唯一可执行的语句是可执行语句."<<endl;#endif/*7.55*/#ifdef NUM755cout << "不是字面值常量类. "#endif/*7.56*/#ifdef NUM756cout<<"静态成员存在于任何对象之外,对象中不包含任何与静态成员相关的数据.不是类的单个对象. ""优点,封装,statis成员可以是私有成员,而全局对象不行.每个对象不需要存储常量数据,如果数据变化了,每个对象就会使用新值. ""不同点,静态成员可以是不完全类型,静态成员可以作为默认实参."#endif/*7.57*/#ifdef NUM757cout <<"见Chapter7.h"<<endl;#endif/*7.58*/#ifdef NUM758Example::rate;Example::vec;cout <<"见编译结果,rate应该是常量表达式,去掉6.5的赋值,vec的静态声明不能指定类中初始化的参数,改为 static vector<double> vec; vector<double>;"<<endl;#endifreturn 0;}
参考资料:
c++ primer中文版第五版,电子工业出版社。
c++ primer第四版习题解答,人民邮电出版社。


0 0
原创粉丝点击