0
点赞
收藏
分享

微信扫一扫

C++ Primer 0x07 练习题解

zibianqu 2022-01-08 阅读 24

📔 C++ Primer 0x07 练习题解

更好的阅读体验

7.1 定义抽象数据类型

7.1.1 定义抽象数据类型

#include <iostream>
#include <string>

struct Sales_data {
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
};

int main(){
	Sales_data total,item;
    double price = 0;
    if (std::cin >> total.bookNo >> total.units_sold >> price){
        total.revenue = total.units_sold * price;
        while (std::cin >> item.bookNo >> item.units_sold >> price){
            item.revenue = item.units_sold * price;
            if (total.bookNo == item.bookNo) {
                total.units_sold += item.units_sold;
                total.revenue += item.revenue;
            }else {
                std::cout << total.bookNo << " " 
                		<< total.units_sold << " "
                		<< total.revenue << std::endl;
                total = item;
            }
        }
        std::cout << total.bookNo << " " 
				<< total.units_sold << " "
				<< total.revenue << std::endl;
    }else {
        std::cerr << "No data?!" << std::endl;
        return -1;
    }
	return 0;
}

7.1.2 定义改进的 Salses_data 类

#include <iostream>
#include <string>

struct Sales_data {
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
	
	Sales_data& combine(const Sales_data &rhs);
	std::string isbn(){return bookNo;}
};

Sales_data& Sales_data::combine(const Sales_data &rhs){
	units_sold += rhs.units_sold;
	revenue += rhs.revenue;
	return *this;
}

int main(){
	Sales_data total,item;
	double price = 0;
    if (std::cin >> total.bookNo >> total.units_sold >> price){
    	total.revenue = total.units_sold * price;
        while (std::cin >> item.bookNo >> item.units_sold >> price){
        	item.revenue = item.units_sold * price;
            if (total.isbn() == item.isbn()) {
                total.combine(item);
            }else {
                std::cout << total.bookNo << " " 
                		<< total.units_sold << " "
                		<< total.revenue << std::endl;
                total = item;
            }
        }
        std::cout << total.bookNo << " " 
				<< total.units_sold << " "
				<< total.revenue << std::endl;
    }else {
        std::cerr << "No data?!" << std::endl;
        return -1;
    }
	return 0;
}
struct Person{
	std::string name;
	std::string address;
};
struct Person{
	std::string name;
	std::string address;
	std::string get_name()const{return name;}
	std::string get_address()const{return address;}
};

应该加,考虑常量对象可能也要使用这些操作

7.1.3 定义类相关的非成员函数

#include <iostream>
#include <string>


struct Sales_data {
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
	
	Sales_data& combine(const Sales_data &rhs);
	std::string isbn()const{return bookNo;}
};

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 = item.units_sold * price;
	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;
}

int main(){
	Sales_data total,item;
    if (read(std::cin,total)){
        while (read(std::cin,item)){
            if (total.isbn() == item.isbn()) {
                total.combine(item);
            }else {
                print(std::cout,total);
                std::cout << std::endl;
                total = item;
            }
        }
        print(std::cout,total);
        std::cout << std::endl;
        
    }else {
        std::cerr << "No data?!" << std::endl;
        return -1;
    }
	return 0;
}

read 会改变Sales_data item的内容,print不会

#include <iostream>
#include <string>

struct Person{
	std::string name;
	std::string address;
	std::string get_name()const{return name;}
	std::string get_address()const{return address;}
};

std::istream& read(std::istream &is,Person &p){
	is >> p.name >> p.address;
	return is;
}

std::ostream& print(std::ostream &os,const Person &p){
	os << p.get_name() << " " << p.get_address();
	return os;
}
int main(){
	Person p;
	read(std::cin,p);
	print(std::cout,p);
	return 0;
}
if (read(read(cin, data1), data2)) //连续读入 data1 data2 的数据

7.1.4 构造函数

#include <iostream>
#include <string>


struct Sales_data {
	Sales_data() = default;
	Sales_data(const std::string &s):bookNo(s){}
	Sales_data(const std::string &s,unsigned u,double p)
		:bookNo(s),units_sold(u),revenue(u*p){}
	Sales_data(std::istream &);
	
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
	
	Sales_data& combine(const Sales_data &rhs);
	std::string isbn()const{return bookNo;}
	double avg_price()const;
};


double Sales_data::avg_price()const{
	return units_sold?(revenue/units_sold):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 = item.units_sold * price;
	return is;
}

std::ostream & print(std::ostream &os,const Sales_data &item){
	os << item.isbn() << " "<< item.units_sold << " "
		<< item.revenue << " " << item.avg_price();
	return os;
}

Sales_data::Sales_data(std::istream &is){
	read(is,*this);
}

Sales_data add(const Sales_data &lhs,const Sales_data &rhs){
	Sales_data sum = lhs;
	sum.combine(rhs);
	return sum;
}

int main(){
	Sales_data A;
	print(std::cout,A) << std::endl;
	
	Sales_data B("bookB");
	print(std::cout,B) << std::endl;
	
	Sales_data C("bookC",12,3.0);
	print(std::cout,C) << std::endl;
	
	Sales_data D(std::cin);
	print(std::cout,D) << std::endl;
	
	return 0;
}
#include <iostream>
#include <string>

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 u,double p)
		:bookNo(s),units_sold(u),revenue(u*p){}
	Sales_data(std::istream &is){read(is,*this);}
	
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
	
	Sales_data& combine(const Sales_data &rhs);
	std::string isbn()const{return bookNo;}
	double avg_price()const;
};


double Sales_data::avg_price()const{
	return units_sold?(revenue/units_sold):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 = item.units_sold * price;
	return is;
}

std::ostream & print(std::ostream &os,const Sales_data &item){
	os << item.isbn() << " "<< item.units_sold << " "
		<< item.revenue << " " << item.avg_price();
	return os;
}

Sales_data add(const Sales_data &lhs,const Sales_data &rhs){
	Sales_data sum = lhs;
	sum.combine(rhs);
	return sum;
}

int main(){
	Sales_data A;
	print(std::cout,A) << std::endl;
	
	Sales_data B("bookB");
	print(std::cout,B) << std::endl;
	
	Sales_data C("bookC",12,3.0);
	print(std::cout,C) << std::endl;
	
	Sales_data D(std::cin);
	print(std::cout,D) << std::endl;
	
	return 0;
}
#include <iostream>
#include <string>


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 u,double p)
		:bookNo(s),units_sold(u),revenue(u*p){}
	Sales_data(std::istream &is){read(is,*this);}
	
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
	
	Sales_data& combine(const Sales_data &rhs);
	std::string isbn()const{return bookNo;}
	double avg_price()const;
};


double Sales_data::avg_price()const{
	return units_sold?(revenue/units_sold):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 = item.units_sold * price;
	return is;
}

std::ostream & print(std::ostream &os,const Sales_data &item){
	os << item.isbn() << " "<< item.units_sold << " "
		<< item.revenue << " " << item.avg_price();
	return os;
}

Sales_data add(const Sales_data &lhs,const Sales_data &rhs){
	Sales_data sum = lhs;
	sum.combine(rhs);
	return sum;
}

int main(){
	Sales_data total(std::cin);
    if (!total.isbn().empty()){
    	std::istream &is = std::cin;
        while (is){
        	Sales_data item(is);
            if (total.isbn() == item.isbn()) {
                total.combine(item);
            }else {
                print(std::cout,total) << std::endl;
                total = item;
            }
        }
        print(std::cout,total) << std::endl;
        
    }else {
        std::cerr << "No data?!" << std::endl;
        return -1;
    }
	
	return 0;
}
Sales_data():bookNo(0),revenue(0){}
#include <iostream>
#include <string>

struct Person;
std::istream& read(std::istream &,Person &);
struct Person{
	Person() = default;
	Person(const std::string n,const std::string a)
		:name(n),address(a){}
	Person(std::istream& is){read(is,*this);}
	
	std::string name;
	std::string address;
	std::string get_name()const{return name;}
	std::string get_address()const{return address;}
};

std::istream& read(std::istream &is,Person &p){
	is >> p.name >> p.address;
	return is;
}

std::ostream& print(std::ostream &os,const Person &p){
	os << p.get_name() << " " << p.get_address();
	return os;
}
int main(){
	Person p(std::cin);
	print(std::cout,p) << std::endl;
	
	Person p2("fishingrod","zj");
	print(std::cout,p2) << std::endl;
	return 0;
}

7.2 访问控制与封装

没有限制

public说明符之后的成员在整个程序内可以被访问,public成员定义类的接口

private说明符之后的成员可以被类的成员函数访问,但是不能使用该类的代码访问,private封装了类的实现细节

使用classstruct定义类的唯一区别就是默认的访问权限,struct默认所有成员是publicclass默认所有成员是private

封装实现了类的接口和实现的分离,隐藏了实现细节,类的用户只能使用接口而无法访问实现部分

封装的两个重要优点

  • 确保用户代码不会无意间破坏封装对象的状态
  • 被封装的类的具体实现可以随时改变,无需调整用户级别的代码

修改了一下

struct Person{
private://数据设为私有,用户不可见	
	std::string name;
	std::string address;
public://get_name、get_address、构造函数公有
	std::string get_name()const{return name;}
	std::string get_address()const{return address;}
    Person() = default;
	Person(const std::string n,const std::string a)
		:name(n),address(a){}
	Person(std::istream& is){read(is,*this);}
};

7.2.1 友元

友元声明让类允许其他类或函数访问它的非公有成员

实际上破坏了封装性和可维护性

#include <iostream>
#include <string>


class Sales_data;
std::istream &read(std::istream&,Sales_data&);

class Sales_data {

friend std::istream & read(std::istream &,Sales_data &);
friend std::ostream & print(std::ostream &,const Sales_data &);
friend Sales_data add(const Sales_data &,const Sales_data &);

private:
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
public:	
	Sales_data& combine(const Sales_data &rhs);
	std::string isbn()const{return bookNo;}
	double avg_price()const;
	
	Sales_data() = default;
	Sales_data(const std::string &s):bookNo(s){}
	Sales_data(const std::string &s,unsigned u,double p)
		:bookNo(s),units_sold(u),revenue(u*p){}
	Sales_data(std::istream &is){read(is,*this);}
};


double Sales_data::avg_price()const{
	return units_sold?(revenue/units_sold):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 = item.units_sold * price;
	return is;
}

std::ostream & print(std::ostream &os,const Sales_data &item){
	os << item.isbn() << " "<< item.units_sold << " "
		<< item.revenue << " " << item.avg_price();
	return os;
}

Sales_data add(const Sales_data &lhs,const Sales_data &rhs){
	Sales_data sum = lhs;
	sum.combine(rhs);
	return sum;
}

int main(){
	Sales_data total(std::cin);
    if (!total.isbn().empty()){
    	std::istream &is = std::cin;
        while (is){
        	Sales_data item(is);
            if (total.isbn() == item.isbn()) {
                total.combine(item);
            }else {
                print(std::cout,total) << std::endl;
                total = item;
            }
        }
        print(std::cout,total) << std::endl;
        
    }else {
        std::cerr << "No data?!" << std::endl;
        return -1;
    }
	
	return 0;
}
#include <iostream>
#include <string>

class Person;
std::istream& read(std::istream &,Person &);
class Person{
friend std::istream& read(std::istream &,Person &);
friend std::ostream& print(std::ostream &,const Person &);

private:	
	std::string name;
	std::string address;

public:
	std::string get_name()const{return name;}
	std::string get_address()const{return address;}
	
	Person() = default;
	Person(const std::string n,const std::string a)
		:name(n),address(a){}
	Person(std::istream& is){read(is,*this);}
};

std::istream& read(std::istream &is,Person &p){
	is >> p.name >> p.address;
	return is;
}

std::ostream& print(std::ostream &os,const Person &p){
	os << p.get_name() << " " << p.get_address();
	return os;
}
int main(){
	Person p(std::cin);
	print(std::cout,p) << std::endl;
	
	Person p2("fishingrod","zj");
	print(std::cout,p2) << std::endl;
	return 0;
}

7.3 类的其他特性

7.3.1 类成员再探

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];}
    Screen &move (pos r,pos c);
private:
	pos cursor = 0;
	pos height = 0, width = 0;
	std::string contents;
};

inline Screen& Screen::move(pos r,pos c){
	pos row = r * width;
	cursor = row + c;
	return *this;
}
class Screen{
public:
	typedef std::string::size_type pos;

public:
	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){}

public:
	char get()const{return contents[cursor];}
	char get(pos r,pos c)const{return contents[r*width+c];}
    Screen &move (pos r,pos c);
    
private:
	pos cursor = 0;
	pos height = 0, width = 0;
	std::string contents;
};

inline Screen& Screen::move(pos r,pos c){
	pos row = r * width;
	cursor = row + c;
	return *this;
}

可以,采用了string能够管理必要的存储空间

  • 某些类不能依赖于合成的版本,当类需要分配类对象之外的资源时,合成的版本常常会失效
  • 很多动态内存的类能(而且应该)使用 vector 对象或者 string 对象管理必要的存储空间,这能避免分配和释放内存带来的复杂性
  • 当含有vector成员对象执行拷贝或赋值操作时,vector会设法拷贝或赋值成员中的元素。销毁时也会一次销毁vector中的每一个元素。这点与 string是非常类似的
inline
double Sales_data::avg_price()const{
	return units_sold?(revenue/units_sold):0;
}

7.3.2 返回 *this 的成员函数

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;
}
#include <iostream>
#include <string>


class Screen{
public:
	typedef std::string::size_type pos;

public:
	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){}

public:
	char get()const{return contents[cursor];}
	char get(pos r,pos c)const{return contents[r*width+c];}
	
    Screen& move(pos r,pos c);
    
    Screen& set(char c);
    Screen& set(pos r,pos col,char ch);
    
    Screen& display(std::ostream &os){
    	do_display(os);return *this;
    }
    const Screen& display(std::ostream &os)const{
    	do_display(os);return *this;
    }
    
private:
	pos cursor = 0;
	pos height = 0, width = 0;
	std::string contents;

private:
	void do_display(std::ostream& os)const{os << contents;}
};

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;
	cursor = row + c;
	return *this;
}

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;
}
myScreen.move(4, 0).set('#').display(std::cout);//move返回副本,set在副本上修改,display展示set修改后的另一个副本
//没有在同一对象上操作
#include <iostream>
#include <string>


class Screen{
public:
	typedef std::string::size_type pos;

public:
	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){}

public:
	char get()const{return contents[cursor];}
	char get(pos r,pos c)const{return contents[r*width+c];}
	
    Screen move(pos r,pos c);
    
    Screen set(char c);
    Screen set(pos r,pos col,char ch);
    
    Screen display(std::ostream &os){
    	do_display(os);return *this;
    }
    const Screen display(std::ostream &os)const{
    	do_display(os);return *this;
    }
    
private:
	pos cursor = 0;
	pos height = 0, width = 0;
	std::string contents;

private:
	void do_display(std::ostream& os)const{os << contents;}
};

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;
	cursor = row + c;
	return *this;
}

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;
}

输出

XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXXXXXXX

优点:

1、使程序意图明确,更易读;

2、可以使形参名和要赋值的成员名相同。

std::string& setName(const string& name)  {  this->name = name; }

缺点:有些场景下比较多余

std::string const& getName() const { return this->name; }

7.3.3 类类型

class Y;

class X{
	Y* point_y;
};

class Y{
	X x_in_y;
};

7.3.4 友元再探

#include <iostream>
#include <string>
#include <vector>

class Screen;
class Window_mgr{
public:
	using ScreenIndex = std::vector<Screen>::size_type;

public:
	void clear(ScreenIndex);

private:
	std::vector<Screen> screens;
};

class Screen{
public:
	typedef std::string::size_type pos;
	friend void Window_mgr::clear(ScreenIndex);

public:
	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){}

public:
	char get()const{return contents[cursor];}
	char get(pos r,pos c)const{return contents[r*width+c];}
	
    Screen move(pos r,pos c);
    
    Screen set(char c);
    Screen set(pos r,pos col,char ch);
    
    Screen display(std::ostream &os){
    	do_display(os);return *this;
    }
    const Screen display(std::ostream &os)const{
    	do_display(os);return *this;
    }
    
private:
	pos cursor = 0;
	pos height = 0, width = 0;
	std::string contents;

private:
	void do_display(std::ostream& os)const{os << contents;}
};

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;
	cursor = row + c;
	return *this;
}

void Window_mgr::clear(ScreenIndex i){
	Screen& s = screens[i];
	s.contents = std::string(s.height*s.width,' ');
}
  • 首先定义 Window_mgr类,其中声明clear函数但不定义,在clear使用Screen的成员之前必须先声明Screen
  • 接下来定义Screen,包括对于clear的友元声明
  • 最后定义clear此时它才可以使用Screen的成员

7.4 类的作用域

pos Screen::size() const
{
    return height * width;
}

如果类内定义报错

extra qualification on member 'size' pos Screen::size()const{return height*width;}

改为

pos size() const
{
    return height * width;
}

如果类外定义报错

error: unknown type name 'pos'pos Screen::size()const

改为

Screen::pos Screen::size()const{
	return height*width;
}

7.4.1 名字查找与类的作用域

找不到 pos,提示未知类型

typedef string Type;
Type initVal(); 
class Exercise {
public:
    typedef double Type;
    Type setVal(Type);
    Type initVal(); 
private:
    int val;
};
Type Exercise::setVal(Type parm) { 
    val = parm + initVal();     
    return val;
}
  • 在类中,如果成员函数使用了外层作用域中的某个名字,而该名字代表一种类型,则类不能在之后重新定义该名字

7.5 构造函数再探

7.5.1 构造函数初始值列表

struct X {
	X (int i, int j): base(i), rem(base % j) {}
	int rem, base;
};
  • 构造函数列表只说明用于初始化成员的值,而不限定初始化的具体顺序
  • 成员的初始化顺序与它们在类定义中的出现顺序一致
struct X {
	X (int i, int j): base(i), rem(base % j) {}
	int base,rem;
};
Sales_data first_item(cin); //Sales_data(std::istream &is){read(is,*this);}
int main() {
	Sales_data next;
    //Sales_data() = default;
    //bookNo = "", cnt = 0, revenue = 0.0
    Sales_data last("9-999-99999-9");
    //	Sales_data(const std::string &s):bookNo(s){} 
    //	bookNo = "9-999-99999-9", cnt = 0, revenue = 0.0
}
Sales_data(std::istream &is = std::cin){read(is,*this);}

不能,一个函数不能既作为重载函数,又作为有默认参数的函数。没法区分

(a) Book
(b) Data
(c) Employee
(d) Vehicle
(e) Object
(f) Tree

(a) Book

class Book{
private:
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
public:
    Sales_data() = default;
	Sales_data(const std::string &s):bookNo(s){}
	Sales_data(const std::string &s,unsigned u,double p)
		:bookNo(s),units_sold(u),revenue(u*p){}
};

7.5.2 委托构造函数

#include <iostream>
#include <string>


class Sales_data;
std::istream &read(std::istream&,Sales_data&);

class Sales_data {

friend std::istream & read(std::istream &,Sales_data &);
friend std::ostream & print(std::ostream &,const Sales_data &);
friend Sales_data add(const Sales_data &,const Sales_data &);

private:
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
public:	
	Sales_data& combine(const Sales_data &rhs);
	std::string isbn()const{return bookNo;}
	double avg_price()const;
	
	Sales_data(const std::string &s,unsigned u,double p)
		:bookNo(s),units_sold(u),revenue(u*p){
			std::cout << "Sales_data(const std::string &s,unsigned u,double p)" << std::endl;
		}
	Sales_data() : Sales_data("",0,0){
		std::cout << "Sales_data()" << std::endl;
	}
	Sales_data(const std::string &s):Sales_data(s,0,0){
		std::cout << "Sales_data(const std::string &s)" << std::endl;
	}
	Sales_data(std::istream &is):Sales_data(){
		read(is,*this);
	}
};


double Sales_data::avg_price()const{
	return units_sold?(revenue/units_sold):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 = item.units_sold * price;
	return is;
}

std::ostream & print(std::ostream &os,const Sales_data &item){
	os << item.isbn() << " "<< item.units_sold << " "
		<< item.revenue << " " << item.avg_price();
	return os;
}

Sales_data add(const Sales_data &lhs,const Sales_data &rhs){
	Sales_data sum = lhs;
	sum.combine(rhs);
	return sum;
}

int main(){
	std::cout << "create A" << std::endl;
	Sales_data A;
	
	std::cout << "create B" << std::endl;
	Sales_data B(std::cin);
	
	std::cout << "create C" << std::endl;
	Sales_data C("123-456-789");
	
	std::cout << "create D" << std::endl;
	Sales_data D("789-456-123",10,2.5);
	
	return 0;
}

输出

create A
Sales_data(const std::string &s,unsigned u,double p)
Sales_data()
create B
Sales_data(const std::string &s,unsigned u,double p)
Sales_data()
create C
Sales_data(const std::string &s,unsigned u,double p)
Sales_data(const std::string &s)
create D
Sales_data(const std::string &s,unsigned u,double p)

见练习7.41

7.5.3 默认构造函数的作用

class NoDefault{
public:
	NoDefault(int i){}
};

class C{
public:
	C():nd(0){}
private:
	NoDefault nd;	
};
vector<NoDefault> vec(10);

不合法,NoDefault没有默认构造函数

合法,有默认构造函数

(a) 不正确,编译器会隐式定义一个合成默认构造函数。

(b) 不正确,给所有参数提供了默认值的构造函数也是默认构造函数。

© 不正确,没意义也要提供初始值

(d) 不正确,要没有定义任何构造函数的时候才会自动生成

7.5.4 隐式的类类型转换

看情况,这节里没有大问题,需要注意的是string参数可能给了一个不存在的bookNo

优点:explicit可以抑制构造函数定义的隐式转换

缺点:如果要转换的话要显示使用构造函数或强制类型转换

string null_isbn("9-999-9999-9");
Sales_data item1(null_isbn);//直接初始化
Sales_data item2("9-999-99999-9");//不合法,只允许一步隐式类类型转换
(a) Sales_data &combine(Sales_data); 
//合法

(b) Sales_data &combine(Sales_data&); 
//不合法,std::string到Sales_data隐式类类型转换超过1次

(c) Sales_data &combine(const Sales_data&) const;
//常量成员函数,不符合combine的用途,要修改
explicit Person(std::istream& is){read(is,*this);}
  • 如果vecotr但参数构造函数不定义成explicit那一个数字转为vecotr<T>就会很奇怪

  • string基本上可以被const char*替换,所以不设为explicit

7.5.5 聚合类

Sales_data item = {"987-0590353403", 25, 15.99};

聚合类才能采用花括号列表初始值的方法初始化,我们可以吧Sales_data改为聚合类

struct Sales_data {//struct 默认public
    std::string bookNo;
    unsigned units_sold;
    double revenue;
};

7.5.6 字面值常量类

class Debug{
public:
	constexpr Debug(bool b = true):hw(b),io(b),other(b){}
	constexpr Debug(bool h,bool i ,bool o)
		:hw(h),io(i),other(o){}
	constexpr bool any(){return hw || io || other;}
	
	void set_io(bool b){io = b;}
	void set_hw(bool b){hw = b;}
	void set_other(bool b){other = b;}

private:
	bool hw;
	bool io;
	bool other;	
};

不应该,constexpr函数必须有且仅有一个return

不是,std::string不是字面值类型

  • 数据成员都是字面值类型的聚合类是字面值常量类
  • 如果一个类不是聚合类但它符合下述要求,则它也是一个字面值常量类
    • 数据成员必须都是字面值类型
    • 类必须至少包含一个constexpr构造函数
    • 如果一个数据成员含有类内初始值,则内置类型成员的初始值必须是一条常量表达式;或者如果成员属于某种类类型,则初始值必须使用成员自己的constexpr构造函数
    • 类必须使用析构函数的默认定义,该成员负责销毁类的对象

7.6 类的静态成员

通过加上关键字static声明类的静态成员,和具体对象不相关,和类相关

静态成员可以用于一些普通成员没法做的场景

  • 静态数据成员可以是不完全类型
  • 静态数据成员的类型可以是它所属的类类型,非静态数据成员只能声明成它所属类的指针或引用
  • 可以使用静态成员作为默认实参,非静态数据成员不可以
class Account{
public:
	void calculate(){amount += amount*interestRate;}
	static double rate(){return interestRate;}
	static void rate(double){interestRate = newRate;}

private:
	std::string owner;
	double amount;
	static double interestRate;
	static constexpr double Rate=13.5;
	static double initRate(){return Rate;}
};

double Account::interestRate = initRate();
// example.h
class Example {
public:
    static  double rate = 6.5;//静态成员类内初始化,初始值必须是常量表达式
    //static  constexpr double rate = 6.5;
    static const int vecSize = 20;
    static vector<double> vec(vecSize);/// vector不需要在类内就定义大小
    //static vector<double> vec;
};

// example.C
#include "example.h"
constexpr double Example::rate;
vector<double> Example::vec(Example::vecSize);
举报

相关推荐

0 条评论