C++ Primer 第七章答案

这篇博客详细解答了C++ Primer第七章的多项习题,涉及Person和Sales_data类的设计,包括成员访问控制、友元函数、构造函数等概念,并探讨了struct和class的使用场景以及封装的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目中所要求的所有类的要求如下:

 

Person.h 

#ifndef _PERSON_H
#define _PERSON_H

#include <iostream>
#include <string>



class Person
{
public:
	Person(std::string name, std::string address) : name(name), address(address) {};
	~Person() {};
	std::string nameInfo() { return name; }
	std::string addressInfo() { return address; }
public:
	std::string name;
	std::string address;

};

std::istream &recordInfno(std::istream&, Person&);
std::ostream &outputInfno(std::ostream&,const Person&);
    
#endif

Person.c

#include "Person.h"

using namespace std;

std::istream &recordInfno(std::istream& is, Person& personInfo)
{
	is >> personInfo.name >> personInfo.address;
	return is;
}
std::ostream &outputInfno(std::ostream& os,const Person& personInfo)
{
	os << personInfo.name << " " << personInfo.address << endl;
	return os;
}



Sales_data.h

#ifndef _SALES_DATA_H
#define _SALES_DATA_H

#include <string>
/*
定义一个实现书店交易的类
它要实现表示一本书的总销售额、售出册数、平均售价
那么要是实现这个类,得先定义其数据成员,然后是类所能提供得接口(实现类所需要的操作)
然后开始实现接口
*/

class Sales_data
{
public:
	Sales_data(std::string number) :bookNo(number), units_sold(0), revenue(0.0){};
	Sales_data(std::istream &is) { read(is, *this); };
    Sales_data() {};
	~Sales_data() {};
	std::string isbn() const { return bookNo; }//返回ISBN编号
	Sales_data& combine(const Sales_data &);     //将一个Sales_date对象加到另一个对象上去
	double avg_price() const; //计算均价
private:
	std::string bookNo;     //ISBN编号
	unsigned units_sold ;//销售数量
	double revenue ;   //销售总额

	friend Sales_data add(const Sales_data&, const Sales_data&); //执行两个Sales_date对象的加法
	friend std::ostream &print(std::ostream&, const Sales_data&);//将Sales_date对象输出到ostream上
	friend std::istream &read(std::istream&, Sales_data&); //从ostream上读入到Sales_date对象
};


//Sale_date 的非成员接口函数
Sales_data add(const Sales_data&, const Sales_data&); //执行两个Sales_date对象的加法
std::ostream &print(std::ostream&, const Sales_data&);//将Sales_date对象输出到ostream上
std::istream &read(std::istream&, Sales_data&); //从ostream上读入到Sales_date对象

#endif

 Sales_data.c

#include "Sales_data.h"
#include <iostream>

using namespace std;

istream &read(std::istream& is, Sales_data& rhs)//输入书的编号 数量 价格
{
	double price = 0;
	is >> rhs.bookNo >> rhs.units_sold >> price;
	rhs.revenue += price * rhs.units_sold;
	return is;
}

ostream &print(std::ostream &os, const Sales_data &rhs)//输出书的编号 数量 总数 平均价格
{
	os << rhs.isbn() << " " << rhs.units_sold << " "
		<< rhs.revenue << " " << rhs.avg_price();
	return os;
}

inline double Sales_data::avg_price() const              //求平均价格
{
	if (units_sold)
		return revenue / units_sold;
	else
		return 0;
}

Sales_data& Sales_data::combine(const Sales_data &rhs)
{
	units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上去
	revenue += rhs.revenue;
	return *this;                //返回调用该函数的对象
}

Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
	Sales_data sum = lhs;
	sum.combine(rhs); //把rhs的数据成员加到sum当中
	return sum;
}

//int main(void)
//{
//	Sales_data total;     //保存当前求和的结果的变量
//	if (read(cin, total)) //读入第一笔交易到total
//	{
//		Sales_data trans;
//		while (read(cin, trans))//读入新的交易到trans
//		{
//			if (total.isbn() == trans.isbn())//如果新读入书编号和前面的相同
//				total.combine(trans);    //执行相加操作
//			else
//			{
//				print(cout, total) << endl; //不相同
//				total = trans;              //处理下一本书
//			}
//		}
//		print(cout, total) << endl;                 //输出最后的交易
//	}
//	else                                                //没有任何交易
//	{
//		cout << "No data?!" << endl;                //通知用户
//	}
//	system("pause");
//	return 0;
//}

Screen.c

#ifndef _SCREEN_H
#define _SCREEN_H

#include <iostream>
#include <string>
class Screen
{
public:
	typedef std::string::size_type pos;
	Screen() = default; //因为Sreen有另一个构造函数
	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) {};
	~Screen() {};
	char get() const
	{
		return contents[cursor];
	}
	char get(pos ht, pos wd) const;
	Screen &move(pos , pos );
	Screen &set(char);
	Screen &set(pos, pos, char);
	Screen &display(std::ostream &os)
	{
		do_display(os); return *this;
	}
	const Screen &display(std::ostream &os) const
	{
		do_display(os); return *this;
	}
	pos size() const;
private:
	void do_display(std::ostream &os) const
	{
		for (size_t i = 0; i < height; i++)
		{
			for (size_t j = 0; j < width; j++)
			{
				os << contents[i*width + j];
			}
			os << "\n";
		}
	}
private:
	pos cursor = 0;
	pos height = 0, width = 1;
	std::string contents;
};

class Employee
{
public:
	Employee() = default;
	Employee(std::string num, std::string name, std::string position) : 
		empNum(num), empName(name), empPosition(name) {};

private:
	std::string empNum;     //工号
	std::string empName;       //名字
	std::string empPosition;   //职位

};
#endif

 Screen.c

#include "Screen.h"

using namespace std;

Screen &Screen::move(pos r, pos c)
{
	pos row = r*width;            //计算行的高度
	cursor = row + c;            //在行内将光标移到指定的列
	return *this;                //以左值的形式返回对象
}

char Screen::get(pos r, pos c) const
{
	pos row = r * width;         //计算的位置    
	return contents[row + c];    //返回给定列的字符
}

Screen &Screen::set(char c)
{
	contents[cursor] = c; //设置当前光标所在位置的新值
	return *this;
}

Screen &Screen::set(pos r, pos col, char ch)
{
	contents[ r * width + col] = ch; //设置当前光标所在位置的新值
	return *this;
}

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

main.c

#include <iostream>
#include "Sales_data.h"
#include "Person.h"
#include "Screen.h"
#include <vector>

using namespace std;

int main(void)
{

	system("pause");
	return 0;
}

 

习题7.1 - 7.3
Sales_data

习题7.4
Person

习题7.5
不应该,名字和住址都可以改

习题7.6 -7.7
Sales_data

习题7.8
read中Sales_data需要被改变,而print中Sales_data不需要

习题7.9
Person

习题7.10
将data1和data2放入同一个缓冲区。

习题7.11 - 7.14
Sales_data

习题7.15
Person

习题7.16
访问说明符出现的位置和次数均无限制,public之后定义的可以被
在整个程序内都可以被访问。而private之后定义的,仅有类内成员访问、
类的友元可以访问

习题7.17
出于统一编程风格的考虑,当我们希望类的所有成员是public时
使用struct,反之,希望成员时private,使用class
以上是书中解释,本人更倾向成员是数据集的时候使用struct,
当成员出现功能性的集合,需要复杂性质的个体操作时使用class。

练习7.18
(1)实现了接口和实现的分离,对类外隐藏实现细节。
(2)确保用户状态不会无意破坏封装对象的状态
(3)被封装的类的具体细节不可改变,而无须调整用户级代码

练习7.19
接口public,数据private

练习7.20
在用户级函数需要访问类内的private成员时声明该函数为友元函数。
友元在使用时可以非常方便的访问非公有成员,但在一定程度上
破了类的封装。

练习7.21

练习7.22

7.25
可以,本类只有简单的数据类型,可以依靠拷贝和赋值操作的默认版本

练习7.28
在函数定义处,加上inline关键字

练习7.27
    Screen myscreen(5, 5, 'x');
    myscreen.move(4, 0).set('#').display(cout);
    cout << "\n";
    myscreen.display(cout);
    cout << "\n";

练习7.28 - 7.29
move返回的不是左值,会导致myscreen实际本身不会被改变。
再一次输出时会发现‘#’未被设置成功。

练习7.30
优点在于明确,假设类外或者成员幻术形参有与类内相同的名字,使用
this明确的指出用的是哪一个。

练习7.31
class y;
class x
{
    y *point_y;
};

class y
{
    x object_x;
};
注:class y需要前向声明,如果没有声明,y是未知类型。

练习7.32

练习7.33
Screen::pos Screen::size() const
{
    return height * width;
}
Screen.cpp里没有定义pos,用户代码需要引用pos,需要Screen::pos 。

练习7.34
pos类型前面的引用将不合法。

练习7.35
类外的使用的是string,类内使用的是double。类内的type将类外的
type隐蔽了。

有一个错误改正为
Exercise::Type Exercise::setVal(Type parm){
...
}

练习7.36
类内初始化是按照定义顺序来的,按照书上写的话
实际上先初始化rem,然而base此时是未初始化的。rem的值将是未定义的
纠正
struct X {
  X (int i, int j): base(i), rem(base % j) { }
  int base, rem;
};

练习7.37

练习7.38
Sales_data(std::istream &is = std::cin) {read(is, *this);}

练习7.39
不合法,Sales_data()将不知道选择那个版本的构造。

练习7.40
class Employee
{
public:
    Employee() = default;
    Employee(std::string num, std::string name, std::string position) : 
        empNum(num), empName(name), empPosition(name) {};

private:
    std::string empNum;     //工号
    std::string empName;       //名字
    std::string empPosition;   //职位

};

练习7.41
委托构造函数本质上是构造函数,只不过它在初始化的时候会使用
本类的另外一个构造函数,即实际上初始化工作是被使用的构造函数
完成的。

练习7.42 
class Employee
{
public:
    Employee() = default;
    Employee(std::string num, std::string name, std::string position) : 
        empNum(num), empName(name), empPosition(name) {};
                Employee(std::string name) :Employee(“0”, name, “ ”) : 
private:
    std::string empNum;     //工号
    std::string empName;       //名字
    std::string empPosition;   //职位

};

练习7.43
class NoDefault {
public:
    NoDefault(int i) {}
};

class C {
public:
    C() : def(0) {} ;
private:
    NoDefault def;
};

练习7.44
不合法,类NoDefault 找不到何时的构造函数初始化。
fix :    vector<NoDefault> vec(10,1);

练习7.45
合法,C有默认实参

练习7.46
(a)错,有default
(b)错
(c)错
(d)错,简单类型可以,复杂的不行,例如一个类含有另一个类的实体。

练习4.47
不该,使用explicit只能直接初始化,不能隐式转换。
这样做
优点:操作方便,使用”“就可以完成初始化

练习4.48 练习4.49 练习4.50

练习4.51
我们通常会用const char* 初始化string,因此不设置explicit

练习4.52

练习7.53
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(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
};

练习7.54
不应该,它需要返回一个变量值

练习7.55
不是

练习7.56
静态成员使用static在类内声明,但它不属于类的任何一个对象。
它并不是在对象被创建时定义。并且应该在类外初始化和定义

练习7.57
class Account {
public:
    void calculate() { amount += amount * interestRate; }
    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();

练习5.58
class Example {
public:
    static constexpr double rate = 6.5;
    static constexpr int vecSize = 20;
    static vector<double> vec(vecSize );
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值