【 声明:版权所有,转载请标明出处,请勿用于商业用途。 联系信箱:[email protected]】
7.1 定义抽象数据类型
1.类的基本思想是数据抽象和封装,数据抽象是一种依赖于接口和实现分离的编程(以及设计)技术。类的接口包括用户所能执行的操作:类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。
2.成员函数通过一个名为this的额外隐式参数来访问调用它的那个对象。当我们调用一个成员函数时,用请求该函数的对象地址初始化this。
3.C++允许把const关键字放在成员函数的参数列表之后,此时,紧跟在参数列表后面的const表示this是一个指向常量的指针。像这样使用const的成员函数被称作常量成员函数。
4.每个类都分别定义了它的对象被初始化的方式,类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做构造函数。构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。
5.如果我们的类没有显式的定义构造函数,那么编译器回味我们隐式的定义一个默认构造函数。编译器创建的构造函数又被称为合成的默认构造函数。对于大多数类来说,这个合成的默认构造函数将按照如下规则初始化类的数据成员:
⑴如果存在类的初始值,用它来初始化成员
⑵否则,默认初始化该成员
6.在C++11新标准中,如果我们需要默认的行为,那么可以通过在参数列表后面写上=default来要求编译器生成构造函数。其中=default 既可以和声明一起出现在类的内部,也可以作为定义出现在类的外部。和其他函数一样,如果=default在类的内部,则默认构造函数是内联的;如果它在类的外部,则该成员默认情况下不是内联的。
7.2 访问控制与封装
1.在C++语言中,我们使用访问说明符加强类的封装性:
⑴定义在public说明符之后的成员在整个程序内可悲访问,public成员定义类的接口
⑵定义在private说明符之后的成员可以被类的成员函数访问,但是不能被使用该类的代码访问,private部分封装了类的实现细节。
2.类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元。如果类想把一个函数作为它的友元,只需要增加一条以friend关键字开始的函数声明语句即可。
7.3 类的其他特性
1.mutable修饰一个可变数据成员,永远不会是const,即使它是const对象的成员。因此,一个const成员函数可以改变一个可变成员的值。
2.一个const成员函数如果以引用的形式返回*this,那么它的返回类型将是常量引用。
7.4 类的作用域
1.名字查找的过程:
⑴首先,在名字所在的块中寻找其声明语句,只考虑在名字的使用之前出现的声明
⑵如果没有找到,继续查找外层作用域
⑶如果最终没有找到匹配的声明,则程序报错
2.类的定义分两步处理
⑴首先,编译成员的声明
⑵直到类全部可见后才编译函数体
3.成员函数中使用的名字按照如下方式解析
⑴首先,在成员函数内查找该名字的声明。和前面一样,只有在函数使用之前出现的声明才被考虑。
⑵如果在成员函数内没有找到,则在类内继续查找,这时类的所有成员都可以被考虑。
⑶如果内类也没有找到该名字的声明,在成员函数定义之前的作用域内继续查找。
7.5 构造函数再探
1.成员的初始化顺序与它们在类定义中的出现顺序一致:第一个成员先被初始化,然后第二个,以此类推。构造函数的初始值列表中初始值的前后位置关系不会影响实际的初始化顺序。
2.一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程,或者说它把它自己的一些职责委托给了其他构造函数。
//非委托构造函数 Sales_data(std::string s,unsigned cnt,double price):bookNo(s),units_sold(cnt),revenue(cnt*price){} //委托构造函数 Sales_data():Sales_data("",0,0){} Sales_data(std::string s):Sales_data(s,0,0){} Sales_data(std::istream &is):Sales_data(){read(is,*this);}
关键字explicit只对一个实参的构造函数有效。需要多个实参的构造函数不能用于执行隐式转换,所以无须将这些构造函数指定为explicit的。只能在类内声明构造函数时使用explicit关键字,在类外部定义时不应重复。
4.聚合类使得用户可以直接访问其成员,并且具有特殊的初始化语法形式。当一个类满足如下条件时,我们说它是聚合的:
⑴所有成员都是public的
⑵没有定义任何构造函数
⑶没有类内初始值
⑷没有基类,没有virtual函数
7.6 类的静态成员
1.静态成员函数也不与任何对象绑定在一起,它们不包含this指针。作为结果,静态成员函数不能声明成const的,而且我们也不能在static函数体内使用this指针。这一限制既使用于this的显式使用,也对调用非静态成员的隐式使用有效。
PS:部分练习答案
练习7.1
#include <iostream> #include <string> using std::cin; using std::cout; using std::endl; using std::string; struct Sales_data { string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; int main() { Sales_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 { std::cerr << "No data?!" << std::endl; return -1; } return 0; }
ex7_02.h
#ifndef CP5_ex7_02_h #define CP5_ex7_02_h #include <string> struct Sales_data { std::string isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&); std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; Sales_data& Sales_data::combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } #endif
练习7.3
#include "ex7_02.h" #include <iostream> using std::cin; using std::cout; using std::endl; int main() { Sales_data total; if(cin>>total.bookNo>>total.units_sold>>total.revenue) { Sales_data trans; while(cin>>trans.bookNo>>trans.units_sold>>trans.revenue) { if(trans.isbn()==total.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 { std::cerr << "No data?!" <<endl; return -1; } return 0; }
练习7.4
ex7_04.h
#ifndef CP5_ex7_04_h #define CP5_ex7_04_h #include <string> class Person { std::string name; std::string address; }; #endif
ex7_05.h
#ifndef CP5_ex7_05_h #define CP5_ex7_05_h #include <string> class Person { std::string name; std::string address; public: std::string& getName() const { return name; } std::string& getAddress() const { return address; } }; #endif
练习7.6
ex7_06.h
#ifndef CP5_ex7_02_h #define CP5_ex7_02_h #include <iostream> #include <string> struct Sales_data { std::string const& isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&); std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; Sales_data& Sales_data::combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } std::istream& read(std::istream& is,Sales_data& item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } std::ostream& print(std::ostream& os,const Sales_data& item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue; return os; } Sales_data add(const Sales_data& lhs,const Sales_data& rhs) { Sales_data sum = lhs; sum.combine(rhs); return sum; } #endif
#include "ex7_06.h" int main() { Sales_data total; if(read(std::cin,total)) { Sales_data trans; while(read(std::cin,trans)) { if(trans.isbn()==total.isbn()) total.combine(trans); else { print(std::cout,total)<<std::endl; total = trans; } } print(std::cout,total)<<std::endl; } else { std::cerr << "No data?!" <<std::endl; return -1; } return 0; }
练习7.8
因为read需要改变类成员的值,而print不需要改变类成员的值
练习7.9
ex7_09.h
#ifndef CP5_ex7_09_h #define CP5_ex7_09_h #include <iostream> #include <string> class Person { std::string name; std::string address; public: const std::string& getName() const { return name; } const std::string& getAddress() const { return address; } }; std::istream& read(std::istream& is,Person& person) { is >> person.name >> person.address; if(!is) person = Person(); return is; } std::ostream& print(std::ostream& os,const Person& person) { os << person.name << " " << person.address; return os; } #endif
练习7.11
ex7_11.h
#ifndef CP5_ex7_11_h #define CP5_ex7_11_h #include <string> #include <iostream> struct Sales_data { Sales_data() = default; Sales_data(const std::string& s):bookNo(s){} Sales_data(const std::string& s,unsigned n,double p):bookNo(s),units_sold(n),revenue(n*p){} Sales_data(std::istream& is); std::string isbn()const {return bookNo;}; Sales_data& combine(const Sales_data&); std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; std::istream& read(std::istream& is,Sales_data& item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } std::ostream& print(std::ostream& os,const Sales_data& item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue; return os; } Sales_data add(const Sales_data& lhs,const Sales_data& rhs) { Sales_data sum = lhs; sum.combine(rhs); return sum; } Sales_data::Sales_data(std::istream& is) { read(is,*this); } Sales_data& Sales_data::combine(Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } #endif
#include "ex7_11.h" int main() { Sales_data item1; print(std::cout,item1) << std::endl; Sales_data item2("12345"); print(std::cout,item2) << std::endl; Sales_data item3("12345",3,20.50); print(std::cout,item3) << std::endl; Sales_data item4(std::cin); print(std::cout,item4) << std::endl; return 0; }
练习7.12
ex7_12.h
#ifndef CP5_ex7_12_h #define CP5_ex7_12_h #include <string> #include <iostream> struct Sales_data; std::istream& read(std::istream&,Sales_data&); struct Sales_data { Sales_data() = default; Sales_data(const std::string& s):bookNo(s){} Sales_data(const std::string& s,unsigned n,double p):bookNo(s),units_sold(n),revenue(n*p){}; Sales_data(std::istream& is){read(is,*this);} std::string isbn() const {return bookNo;}; Sales_data& combine(const Sales_data&); std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; Sales_data& Sales_data::combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } std::istream& read(std::istream& is,Sales_data& item) { double price = 0.0; is >> item.bookNo >> item.units_sold >> price; item.revenue = item.units_sold*price; return is; } std::ostream& print(std::ostream& os,Sales_data& item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue; return os; } Sales_data add(const Sales_data& lhs,const Sales_data& rhs) { Sales_data sum = lhs; sum.combine(rhs); return sum; } #endif
#include "ex7_12.h" int main() { Sales_data total(std::cin); if(!total.isbn().empty()) { std::istream& is = std::cin; while(is) { Sales_data trans(is); if(total.isbn() == trans.isbn()) total.combine(trans); else { print(std::cout,total) << std::endl; total = trans; } } } else { std::cerr << "No data?!" << std::endl; return -1; } return 0; }
练习7.15
ex7_15.h
#ifndef CP5_ex7_15_h #define CP5_ex7_15_h #include <string> #include <iostream> struct Person; std::istream& read(std::istream&,Person&); struct Person { Person() = default; Person(const std::string sname,const std::string saddr):name(sname),address(saddr){} Person(std::istream& is){read(is,*this);} std::string getName() const{return name;} std::string getAddress() const{return address;} std::string name; std::string address; }; std::istream& read(std::istream& is,Person& item) { is >> item.name >> item.address; return is; } std::ostream& print(std::ostream& os,const Person& item) { os << item.name << " " << item.address; return os; } #endif
一个类可以包含0个或多个访问说明符,而且对于某个访问说明符能出现多少次也没有严格限定。每个访问说明符指定了接下来的成员的访问级别,其有效范围直到出现下一个访问说明符或者到达类的结尾处位置。
允许全局可见的部分,如构造函数和部分成员函数紧跟在public说明符之后。
只允许类的成员可见的部分,如数据成员和作为实现部分的函数跟在private说明符后面。
练习7.17
区别是它们的默认访问级别,class默认是private,struct默认是public
练习7.21
ex7_21.h
#ifndef CP5_ex7_21_h #define CP5_ex7_21_h #include <string> #include <iostream> class Sales_data { friend std::istream& read(std::istream& is,Sales_data& item); friend std::ostream& print(std::ostream& os,const Sales_data& item); friend Sales_data add(Sales_data& lhs,Sales_data& rhs); public: Sales_data() = default; Sales_data(const std::string& s):bookNo(s){} Sales_data(const std::string& s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){} Sales_data(std::istream& is){read(is,*this);} std::string isbn() const{return bookNo;}; Sales_data& combine(const Sales_data&); private: std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; Sales_data& combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } std::istream& read(std::istream& is,Sales_data& item) { double price = 0.0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } std::ostream& print(std::ostream& os,const Sales_data& item) { os << item.bookNo << " " << item.units_sold << " " << item.revenue; return os; } Sales_data& add(const Sales_data& lhs,const Sales_data& rhs) { Sales_data sum = lhs; sum.combine(rhs); return sum; } #endif
练习7.22
ex7_22.h
#ifndef CP5_ex7_22_h #define CP5_ex7_22_h #include <string> #include <iostream> class Person { friend std::istream& read(std::istream& is,Person& person); friend std::ostream& print(std::ostream& os,const Person& person); public: Person() = default; Person(const std::string& sname,const std::string& saddr):name(sname),address(saddr){} Person(std::istream& is){read(is,*this);} std::string getName() const {return name;} std::string getAdress() const {return address;} private: std::string name; std::string address; }; std::istream& read(std::istream& is,Person& person) { is >> person.name >> person.address; return is; } std::ostream& print(std::ostream& os,const Person& person) { os << person.name << " " << person.address; return os; } #endif
练习7.23
ex7_23.h
#ifndef CP5_ex7_23_h #define CP5_ex7_23_h #include <string> 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];} char get(pos r,pos c) const {return contents[r*width+c];} private: pos cursor = 0; pos height = 0,width = 0; std::string contents; } #endif
ex7_24.h
#ifndef CP5_ex7_24_h #define CP5_ex7_24_h #include <string> 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];} char get(pos r,pos c) const {return contents[r*width+c];} private: pos cursor = 0; pos height = 0,width = 0; std::string contents; } #endif
ex7_26.h
#ifndef CP5_ex7_26_h #define CP5_ex7_26_h #include <string> #include <iostream> class Sales_data { friend std::istream& read(std::istream& is,Sales_data& item); friend std::ostream& print(std::ostream& os,const Sales_data& item); friend Sales_data add(Sales_data& lhs,Sales_data& rhs); public: Sales_data() = default; Sales_data(const std::string& s):bookNo(s){} Sales_data(const std::string& s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){} Sales_data(std::istream& is){read(is,*this);} std::string isbn() const{return bookNo;}; Sales_data& combine(const Sales_data&); private: inline double avg_price()const; private: std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; Sales_data& Sales_data::combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } std::istream& read(std::istream& is,Sales_data& item) { double price = 0.0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } std::ostream& print(std::ostream& os,const Sales_data& item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue; return os; } Sales_data& add(const Sales_data& lhs,const Sales_data& rhs) { Sales_data sum = lhs; sum.combine(rhs); return sum; } inline double Sales_data::avg_price() const { return units_sold ? revenue / units_sold : 0; } #endif
练习7.27
ex7_27.h
#ifndef CP5_ex7_27_h #define CP5_ex7_27_h #include <string> #include <iostream> 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() { return contents[cursor]; } char get(pos r,pos c) { return contents[r*width+c]; } inline Screen& move(pos r,pos c); inline Screen& set(char c); inline Screen& set(pos r,pos c,char ch); const Screen& display(std::ostream& os) const { do_display(os); return *this; } Screen& display(std::ostream& os) { do_display(os); return *this; } private: void do_display(std::ostream& os) const { os << contents; } private: pos cursor = 0; pos height = 0,width = 0; std::string contents; }; inline Screen& Screen::move(pos r,pos c) { cursor = r*width+c; return *this; } inline Screen& Screen::set(char c) { contents[cursor] = c; return *this; } inline Screen& Screen::set(pos r,pos c,char ch) { contents[r*width+c] = ch; return *this; } #endif
#include "ex7_27.h" int main() { Screen myScreen(5, 5, 'X'); myScreen.move(4, 0).set('#').display(std::cout); std::cout << "\n"; myScreen.display(std::cout); std::cout << "\n"; return 0; }
练习7.31
#ifndef CP5_ex7_31_h #define CP5_ex7_31_h class Y; class X { Y* y = nullptr; }; class Y { X x; }; #endif
#ifndef CP5_ex7_32_h #define CP5_ex7_32_h #include <vector> #include <iostream> #include <string> class Screen; class Window_mgr { public: using ScreenIndex = std::vector<Screen>::size_type; inline void clear(ScreenIndex); private: std::vector<Screen> screens; }; class Screen { friend void Window_mgr::clear(ScreenIndex); public: using pos = std::string::size_type; 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];} char get(pos r,pos c) const {return contents[r*width+c];} inline Screen& move(pos r,pos c); inline Screen& set(char ch); inline Screen& set(pos r,pos c,char ch); const Screen& display(std::ostream& os)const { do_display(os); return *this; } Screen& display(std::ostream& os) { do_display(os); return *this; } private: void do_display(std::ostream& os) const {os<<contents;} private: pos cursor = 0; pos height = 0,width = 0; std::string contents; }; inline void Window_mgr::clear(ScreenIndex i) { if(i>=screens.size()) return; Screen &s = screens[i]; s.contents = std::string(s.heigth*s.width,' '); } inline Screen& Screen::move(pos r,pos c) { cursor = r*width+c; return *this; } inline Screen& Screen::set(char ch) { contents[cursor] = ch; return *this; } inline Screen& Screen::set(pos r,pos c,char ch) { contents[r*width+c] = ch; return *this; } #endif
练习7.33
pos类型没有定义,应改为
Screen::pos Screen::size() const { return height*width; }
练习7.35
Type Exercise::setVal(Type parm) { val = parm + initVal(); return val; }
Exercise::Type Exercise::setVal(Type parm) { val = parm + initVal(); return val; }
且
Exercise::initVal()
需要定义
练习7.36
在这里rem会先于base被初始化,所以我们不能使用未初始化的base来对rem进行初始化
struct X { X (int i, int j): base(i), rem(base % j) { } int base, rem; };
ex7_41.h
#ifndef CP5_ex7_41_h #define CP5_ex7_41_h #include <string> #include <iostream> class Sales_data { friend std::istream& read(std::istream& is,Sales_data& item); friend std::ostream& print(std::ostream& os,const Sales_data& item); friend Sales_data add(const Sales_data& lhs,const Sales_data& rhs); public: Sales_data(const std::string& s,unsigned n,double p):bookNo(s),units_sold(n),revenue(n*p) { std::cout << "Sales_data(const std::string&,unsigned,double)" << std::endl; } Sales_data():Sales_data("",0,0.0f) { std::cout << "Sales_data()" << std::endl; } Sales_data(const std::string& s):Sales_data(s,0,0.0f) { std::cout << "Sales_data(const std::string&)" << std::endl; } Sales_data(std::istream& is); std::string isbn()const { return bookNo; } Sales_data& combine(const Sales_data&); private: inline double avg_price()const; private: std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; inline double Sales_data::avg_price() const { return units_sold?revenue/units_sold:0; } std::istream& read(std::istream& is,const Sales_data& item); std::ostream& print(std::ostream& os,const Sales_data& item); Sales_data add(const Sales_data& lhs,const Sales_data& rhs); #endifex7_41.cpp
#include "ex7_41.h" Sales_data::Sales_data(std::istream& is):Sales_data() { std::cout << "Sales_data(std::istream&)" << std::endl; read(is,*this); } Sales_data& Sales_data::combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } std::istream& read(std::istream& is,Sales_data& item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } std::ostream& print(std::ostream& os,const Sales_data& item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue; return os; } Sales_data add(const Sales_data& lhs,const Sales_data& rhs) { Sales_data sum = lhs; sum.combine(rhs); return sum; }
main.cpp
#include "ex7_41.h" using std::cout; using std::endl; int main() { cout << "1. default way: " << endl; cout << "----------------" << endl; Sales_data s1; cout << "\n2. use std::string as parameter: " << endl; cout << "----------------" << endl; Sales_data s2("CPP-Primer-5th"); cout << "\n3. complete parameters: " << endl; cout << "----------------" << endl; Sales_data s3("CPP-Primer-5th", 3, 25.8); cout << "\n4. use istream as parameter: " << endl; cout << "----------------" << endl; Sales_data s4(std::cin); return 0; }
练习7.43
#include <vector> class NoDefault { public: NoDefault(int i){} }; class C { public: C():def(0){} private: NoDefault def; }; int main() { C c; std::vector<C> vec(10); return 0; }
练习7.53
#include CP5_ex7_53_h #include CP5_ex7_53_h class 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(o){} 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; bool io; bool other; } #endif
#ifndef CP5_ex7_57_h #define CP5_ex7_57_h #include <string> class Account { public: void calculate{amount += amount * interstRate;} static double rate(){return interestRate;} static void rate(double newRate){interestRate = newRate;} private: std::string owner; double amount; static double interestRate; static constexpr double todayRate = 42.42; static double initRate(){return todayRate;} } double Account::interestRate = initRate(); #endif