在我自学C++过程中,我选择了C++Primer这本书,并对部分代码习题进行了求解以及运行结果。接下来几个月我将为大家定时按章节更新习题答案与运行结果,运行环境(Visual Studio Code,windows 11):
C++ Primer (第五版)习题答案总目录:
C++ Primer (第五版)习题答案总目录-CSDN博客
目录
7.26 将Sales_data::avg_price 定义成内联函数。
7.27 给你自己的Screen 类添加move、set 和display 函数,通过执行下面的代码检验你的类是否正确。
7.29 修改你的Screen 类,令move、set和display函数返回Screen并检查程序的运行结果,在上一个练习中你的推测正确吗?
7.31 定义一对类X 和Y,其中X 包含一个指向 Y 的指针,而Y 包含一个类型为 X 的对象。
7.32 定义你自己的Screen 和 Window_mgr,其中clear是Window_mgr的成员,是Screen的友元。
7.33 如果我们给Screen 添加一个如下所示的size成员将发生什么情况?如果出现了问题,请尝试修改它。
7.35 解释下面代码的含义,说明其中的Type和initVal分别使用了哪个定义。如果代码存在错误,尝试修改它。
7.36 下面的初始值是错误的,请找出问题所在并尝试修改它。
7.37 使用本节提供的Sales_data类,确定初始化下面的变量时分别使用了哪个构造函数,然后罗列出每个对象所有的数据成员的值。
7.38 有些情况下我们希望提供cin作为接受istream& 参数的构造函数的默认实参,请声明这样的构造函数。
7.40 从下面的抽象概念中选择一个(或者你自己指定一个),思考这样的类需要哪些数据成员,提供一组合理的构造函数并阐明这样做的原因。
7.43 假定有一个名为 NoDefault 的类,它有一个接受 int 的构造函数,但是没有默认构造函数。定义类 C,C 有一个 NoDefault 类型的成员,定义C 的默认构造函数。
7.48 假定Sales_data 的构造函数不是explicit的,则下述定义将执行什么样的操作?
7.50 确定在你的Person 类中是否有一些构造函数应该是 explicit 的。
7.52 使用2.6.1节的 Sales_data 类,解释下面的初始化过程。如果存在问题,尝试修改它。
7.56 什么是类的静态成员?它有何优点?静态成员与普通成员有何区别。
7.58 下面的静态数据成员的声明和定义有错误吗?请解释原因。
7.23 编写你自己的Screen 类型。
#pragma once
#ifndef SCREEN_H_
#define SCREEN_H_
#include <string>
using namespace std;
class Screen
{
public:
using pos = string::size_type;
Screen() = default;
Screen(pos ht, pos wd, char c) :height(ht), width(wd), contents(ht* wd, c)
{
}
char get(pos r,pos c) const
{
return contents[r * width + c];
}
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
};
#endif // !SCREEN_H_
7.24 给你的Screen 类添加三个构造函数:一个默认构造函数;另一个构造函数接受宽和高的值,然后将contents 初始化成给定数量的空白;第三个构造函数接受宽和高的值以及一个字符,该字符作为初始化后屏幕的内容。
#pragma once
#ifndef SCREEN_H_
#define SCREEN_H_
#include <string>
using namespace std;
class Screen
{
public:
using pos = string::size_type;
Screen() = default;
Screen(pos ht, pos wd) :height(ht), width(wd)
{
}
Screen(pos ht, pos wd, char c) :height(ht), width(wd), contents(ht* wd, c)
{
}
char get(pos r, pos c) const
{
return contents[r * width + c];
}
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
};
#endif // !SCREEN_H_
7.26 将Sales_data::avg_price 定义成内联函数。
#pragma once
#ifndef SALESDATA_7_2_H_
#define SALESDATA_7_2_H_
#include <string>
#include <iostream>
using namespace std;
struct Salesdata;
istream& read(istream& is, Salesdata& item);
Salesdata add(const Salesdata& item1, const Salesdata& item2);
ostream& print(ostream& os, const Salesdata& item);
struct Salesdata
{
friend istream& read(istream& is, Salesdata& item);
friend Salesdata add(const Salesdata& item1, const Salesdata& item2);
friend ostream& print(ostream& os, const Salesdata& item);
public:
string isbn() const
{
return bookNo;
}
Salesdata& combine(const Salesdata&);
Salesdata() = default; //此种情况,指令编译器执行合成的默认构造函数来初始化
Salesdata(const string& s) : bookNo(s)
{ }; //构造函数中未给定初始值的部分,利用数据成员初始值进行初始化
Salesdata(const string& s, unsigned n, double p) : bookNo(s), units_sold(n), revenue(n* p)
{ };
Salesdata(istream& is)
{
read(is, *this);
}
private:
inline double avg_price() const;
string bookNo;
unsigned units_sold = 0;
double revenue = 0;
};
Salesdata& Salesdata::combine(const Salesdata& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
inline double Salesdata::avg_price() const
{
if (units_sold)
{
return revenue / units_sold;
}
else
return 0;
}
istream& read(istream& is, Salesdata& item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = item.units_sold * price;
return is;
}
ostream& print(ostream& os, const Salesdata& item) //因为IO类属于不能被拷贝的类型,因此只能通过引用来传递它们
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price() << endl;
return os;
}
Salesdata add(const Salesdata& item1, const Salesdata& item2)
{
Salesdata sum = item1;
sum.combine(item2);
return sum;
}
#endif // !SALESDATA_7_2_H_
.cpp
#include <iostream>
#include <string>
#include "SalesData_7_2.h"
using namespace std;
int main()
{
Salesdata total(cin);
if (!total.isbn().empty())
{
while(cin)
{
Salesdata trans(cin);
if (total.isbn() == trans.isbn())
{
total.combine(trans);
}
else
{
print(cout, total);
total = trans;
}
}
}
else
{
std::cerr << "No data?!" << std::endl;
return -1;
}
return 0;
}
7.27 给你自己的Screen 类添加move、set 和display 函数,通过执行下面的代码检验你的类是否正确。
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.display(cout);
cout << "\n";
.h
#pragma once
#ifndef SCREEN_H_
#define SCREEN_H_
#include <string>
using namespace std;
class Screen
{
public:
using pos = string::size_type;
Screen() = default;
Screen(pos ht, pos wd) :height(ht), width(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];
}
Screen& move(pos r, pos c);
Screen& set(char);
Screen& set(pos, pos, char);
Screen& display(ostream& os)
{
do_display(os);
return *this;
}
const Screen& display(ostream& os) const
{
do_display(os);
return *this;
}
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
void do_display(ostream& os) const
{
os << contents;
}
};
inline Screen& Screen::move(pos r, pos c)
{
pos row = r * width;
cursor = row + c;
return *this;
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
inline Screen& Screen::set(pos r, pos col, char c)
{
contents[r * width + col] = c;
return *this;
}
#endif // !SCREEN_H
.cpp
#include <iostream>
#include <string>
#include "SalesData_7_2.h"
#include "Screen.h"
using namespace std;
int main()
{
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.display(cout);
cout << "\n";
return 0;
}
7.29 修改你的Screen 类,令move、set和display函数返回Screen并检查程序的运行结果,在上一个练习中你的推测正确吗?
.h
#pragma once
#ifndef SCREEN_H_
#define SCREEN_H_
#include <string>
using namespace std;
class Screen
{
public:
using pos = string::size_type;
Screen() = default;
Screen(pos ht, pos wd) :height(ht), width(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];
}
Screen move(pos r, pos c);
Screen set(char);
Screen set(pos, pos, char);
Screen display(ostream& os)
{
do_display(os);
return *this;
}
const Screen display(ostream& os) const
{
do_display(os);
return *this;
}
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
void do_display(ostream& os) const
{
os << contents;
}
};
inline Screen Screen::move(pos r, pos c)
{
pos row = r * width;
cursor = row + c;
return *this;
}
inline Screen Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
inline Screen Screen::set(pos r, pos col, char c)
{
contents[r * width + col] = c;
return *this;
}
#endif // !SCREEN_H_
.cpp
#include <iostream>
#include <string>
#include "SalesData_7_2.h"
#include "Screen.h"
using namespace std;
int main()
{
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.display(cout);
cout << "\n";
return 0;
}
7.31 定义一对类X 和Y,其中X 包含一个指向 Y 的指针,而Y 包含一个类型为 X 的对象。
class Y;
class X{
Y* y = nullptr;
};
class Y{
X x;
};
7.32 定义你自己的Screen 和 Window_mgr,其中clear是Window_mgr的成员,是Screen的友元。
#pragma once
#ifndef SCREEN_H_
#define SCREEN_H_
#include <string>
#include <vector>
using namespace std;
class Screen;
class window_mgr
{
public:
using ScreenIndex = vector<Screen>::size_type;
void clear(ScreenIndex);
private:
vector<Screen> screens;
};
class Screen
{
friend void window_mgr::clear(ScreenIndex);
public:
using pos = string::size_type;
Screen() = default;
Screen(pos ht, pos wd) :height(ht), width(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];
}
Screen move(pos r, pos c);
Screen set(char);
Screen set(pos, pos, char);
Screen display(ostream& os)
{
do_display(os);
return *this;
}
const Screen display(ostream& os) const
{
do_display(os);
return *this;
}
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
void do_display(ostream& os) const
{
os << contents;
}
};
inline Screen Screen::move(pos r, pos c)
{
pos row = r * width;
cursor = row + c;
return *this;
}
inline Screen Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
inline Screen Screen::set(pos r, pos col, char c)
{
contents[r * width + col] = c;
return *this;
}
void window_mgr::clear(ScreenIndex i)
{
Screen& s = screens[i];
s.contents = string(s.height * s.width, ' ');
}
#endif // !SCREEN_H_
7.33 如果我们给Screen 添加一个如下所示的size成员将发生什么情况?如果出现了问题,请尝试修改它。
pos Screen::size() const
{
return height * width;
}
.h
#pragma once
#ifndef SCREEN_H_
#define SCREEN_H_
#include <string>
#include <vector>
using namespace std;
class Screen;
class window_mgr
{
public:
using ScreenIndex = vector<Screen>::size_type;
void clear(ScreenIndex);
private:
vector<Screen> screens;
};
class Screen
{
friend void window_mgr::clear(ScreenIndex);
public:
using pos = string::size_type;
Screen() = default;
Screen(pos ht, pos wd) :height(ht), width(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];
}
Screen move(pos r, pos c);
Screen set(char);
Screen set(pos, pos, char);
Screen display(ostream& os)
{
do_display(os);
return *this;
}
const Screen display(ostream& os) const
{
do_display(os);
return *this;
}
pos size() const;
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
void do_display(ostream& os) const
{
os << contents;
}
};
inline Screen Screen::move(pos r, pos c)
{
pos row = r * width;
cursor = row + c;
return *this;
}
inline Screen Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
inline Screen Screen::set(pos r, pos col, char c)
{
contents[r * width + col] = c;
return *this;
}
Screen::pos Screen::size() const
{
return height * width;
}
void window_mgr::clear(ScreenIndex i)
{
Screen& s = screens[i];
s.contents = string(s.height * s.width, ' ');
}
#endif // !SCREEN_H_
.cpp
#include <iostream>
#include <string>
#include "SalesData_7_2.h"
#include "Screen.h"
using namespace std;
int main()
{
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << endl;
myScreen.display(cout);
cout << endl;
cout << myScreen.size() << endl;
return 0;
}
7.35 解释下面代码的含义,说明其中的Type和initVal分别使用了哪个定义。如果代码存在错误,尝试修改它。
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;
}
在类中,如果成员使用了外层作用域中的某个名字,而该名字代表一种类型,则类不能在之后重新定义该名字。因此重复定义 Type 是错误的行为。
Exercise::Type Exercise::setVal(Type parm) {
val = parm + initVal();
return val;
}
7.36 下面的初始值是错误的,请找出问题所在并尝试修改它。
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;
};
7.37 使用本节提供的Sales_data类,确定初始化下面的变量时分别使用了哪个构造函数,然后罗列出每个对象所有的数据成员的值。
Sales_data first_item(cin); // use Sales_data(std::istream &is) ;
int main()
{
Sales_data next; // use Sales_data(std::string s = "");
Sales_data last("9-999-99999-9"); // use Sales_data(std::string s = "");
}
7.38 有些情况下我们希望提供cin作为接受istream& 参数的构造函数的默认实参,请声明这样的构造函数。
Sales_data(std::istream &is = std::cin) { read(is, *this); }
不合法。当你调用 Sales_data()
构造函数时,无法区分是哪个重载。
7.40 从下面的抽象概念中选择一个(或者你自己指定一个),思考这样的类需要哪些数据成员,提供一组合理的构造函数并阐明这样做的原因。
(a) Book
(b) Data
(c) Employee
(d) Vehicle
(e) Object
(f) Tree
Book
//by Mooophy
#include <iostream>
#include <string>
using namespace std;
class Book
{
public:
Book(unsigned isbn, string const& name, string const& author, string const& pubdate)
:isbn_(isbn), name_(name), author_(author), pubdate_(pubdate)
{ }
explicit Book(istream &in)
{
in >> isbn_ >> name_ >> author_ >> pubdate_;
}
private:
unsigned isbn_;
string name_;
string author_;
string pubdate_;
};
7.41 使用委托构造函数重新编写你的Sales_data 类,给每个构造函数体添加一条语句,令其一旦执行就打印一条信息。用各种可能的方式分别创建 Sales_data 对象,认真研究每次输出的信息直到你确实理解了委托构造函数的执行顺序。
.h
#pragma once
#ifndef SALESDATA_7_2_H_
#define SALESDATA_7_2_H_
#include <string>
#include <iostream>
using namespace std;
struct Salesdata;
istream& read(istream& is, Salesdata& item);
Salesdata add(const Salesdata& item1, const Salesdata& item2);
ostream& print(ostream& os, const Salesdata& item);
struct Salesdata
{
friend istream& read(istream& is, Salesdata& item);
friend Salesdata add(const Salesdata& item1, const Salesdata& item2);
friend ostream& print(ostream& os, const Salesdata& item);
public:
string isbn() const
{
return bookNo;
}
Salesdata& combine(const Salesdata&);
//Salesdata(const string& s) : bookNo(s)
//{ }; //构造函数中未给定初始值的部分,利用数据成员初始值进行初始化
Salesdata(const string& s, unsigned n, double p) : bookNo(s), units_sold(n), revenue(n* p)
{
cout << "Salesdata(const string& s, unsigned n, double p)!" << endl;
};
Salesdata() :Salesdata("", 0, 0)
{
cout << "Salesdata() :Salesdata("", 0, 0)!" << endl;
}
Salesdata(const string& s) :Salesdata(s, 0, 0)
{
cout << "Salesdata(const string& s) :Salesdata(s, 0, 0)!" << endl;
}
Salesdata(istream& is):Salesdata()
{
read(is, *this);
cout << "Salesdata(istream & is) :Salesdata()" << endl;
}
private:
inline double avg_price() const;
string bookNo;
unsigned units_sold = 0;
double revenue = 0;
};
Salesdata& Salesdata::combine(const Salesdata& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
inline double Salesdata::avg_price() const
{
if (units_sold)
{
return revenue / units_sold;
}
else
return 0;
}
istream& read(istream& is, Salesdata& item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = item.units_sold * price;
return is;
}
ostream& print(ostream& os, const Salesdata& item) //因为IO类属于不能被拷贝的类型,因此只能通过引用来传递它们
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price() << endl;
return os;
}
Salesdata add(const Salesdata& item1, const Salesdata& item2)
{
Salesdata sum = item1;
sum.combine(item2);
return sum;
}
#endif // !SALESDATA_7_2_H_
.cpp
#include <iostream>
#include <string>
#include "SalesData_7_2.h"
#include "Screen.h"
using namespace std;
int main()
{
Salesdata s1("001-01", 1, 100);
Salesdata s2;
Salesdata s3("001-02");
Salesdata s4(cin);
return 0;
}
7.42 对于你在练习7.40中编写的类,确定哪些构造函数可以使用委托。如果可以的话,编写委托构造函数。如果不可以,从抽象概念列表中重新选择一个你认为可以使用委托构造函数的,为挑选出的这个概念编写类定义。
class Book
{
public:
Book(unsigned isbn, std::string const& name, std::string const& author, std::string const& pubdate)
:isbn_(isbn), name_(name), author_(author), pubdate_(pubdate)
{ }
Book(unsigned isbn) : Book(isbn, "", "", "") {}
explicit Book(std::istream &in)
{
in >> isbn_ >> name_ >> author_ >> pubdate_;
}
private:
unsigned isbn_;
std::string name_;
std::string author_;
std::string pubdate_;
};
7.43 假定有一个名为 NoDefault 的类,它有一个接受 int 的构造函数,但是没有默认构造函数。定义类 C,C 有一个 NoDefault 类型的成员,定义C 的默认构造函数。
class NoDefault {
public:
NoDefault(int i)
{ }
};
class C {
public:
C() : def(0)
{ }
private:
NoDefault def;
};
7.48 假定Sales_data 的构造函数不是explicit的,则下述定义将执行什么样的操作?
string null_isbn("9-999-9999-9");
Sales_data item1(null_isbn);
Sales_data item2("9-999-99999-9");
All right!
7.50 确定在你的Person 类中是否有一些构造函数应该是 explicit 的。
#pragma once
#ifndef PERSON_H_
#define PERSON_H_
#include <string>
#include <iostream>
using namespace std;
struct Person;
istream& read(istream& is, Person& item);
struct Person
{
friend istream& read(istream& is, Person& item);
friend ostream& print(ostream& os, const Person& item);
private:
string name;
string address;
public:
string getname() const
{
return name;
}
string getaddress() const
{
return address;
}
Person() = default;
explicit Person(istream& is) { read(is, *this); };
explicit Person(string& NM) : name(NM) { };
Person(string& NM, string& ADD) : name(NM), address(ADD) { }
};
istream& read(istream& is, Person& item)
{
double price = 0;
is >> item.name >> item.address ;
return is;
}
ostream& print(ostream& os, const Person& item) //因为IO类属于不能被拷贝的类型,因此只能通过引用来传递它们
{
os << item.name << " " << item.address << endl;
return os;
}
#endif // !PERSON_H_
7.52 使用2.6.1节的 Sales_data 类,解释下面的初始化过程。如果存在问题,尝试修改它。
Sales_data item = {"987-0590353403", 25, 15.99};
struct Sales_data {
std::string bookNo;
unsigned units_sold;
double revenue;
};
7.53 定义你自己的 Debug。
#pragma once
#ifndef DEBUG_H
#define DEBUG_H
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 amy()
{
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;
};
#endif DEBUG_H
7.56 什么是类的静态成员?它有何优点?静态成员与普通成员有何区别。
类的静态成员与类本身直接相关,而不是与类的各个对象保持关联。
每个对象不需要存储公共数据,如果数据被改变,则每个对象都可以使用新值。
静态数据成员可以是不完全类型;
可以使用静态成员作为默认实参。
7.57 编写你自己的 Account 类。
#pragma once
#ifndef ACCOUNT_H
#define ACCOUNT_H
#include <string>
using namespace std;
class Account
{
public:
void calculate()
{
amount += amount * interestRate;
}
static double rate()
{
return interestRate;
}
static void rate(double newRate)
{
interestRate = newRate;
}
private:
string owner;
double amount;
static double interestRate;
static double initRate()
{
return 4.0;
}
};
double Account::interestRate = initRate();
#endif // !ACCOUNT_H
7.58 下面的静态数据成员的声明和定义有错误吗?请解释原因。
//example.h
class Example {
public:
static double rate = 6.5;
static const int vecSize = 20;
static vector<double> vec(vecSize);
};
//example.c
#include "example.h"
double Example::rate;
vector<double> Example::vec;
除了静态化常量,别的静态化成员无法在类内定义。
// example.h
class Example {
public:
static double rate;
static const int vecSize = 20;
static vector<double> vec;
};
// example.C
#include "example.h"
double Example::rate = 6.5;
vector<double> Example::vec(vecSize);
//vector<double> Example::vec(Example::vecSize);