c++ primer 第五版----第七章习题解答

7.1  使用2.6.1节练习定义的Sales_data类为1.6节(第21页)的交易处理程序编写一个新版本。

#include<iostream>
#include<vector>
#include<string>
using namespace std;
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;
			}
			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;
	}

	return 0;
}

7.2-7.3  曾在2.6.2节的练习(第76页)中编写了一个Sales_data类,请向这个类添加combine和isbn成员。

#include<iostream>
#include<vector>
#include<string>
using namespace std;
struct Sales_data
{
	string isbn() const { return bookNo; }
	Sales_data& combine(const Sales_data&);
	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;
}
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.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;
	}

	return 0;
}

7.4 -7.5   编写一个名为Person的类,使其表示人员的姓名和住址。使用string对象存放这些元素,接下来的练习将不断充实这个类的其他特征。

struct Person
{
	string name_num() const { return name; }
	string address_num()  { return address; }//地址可变,所以不是const
	string name;
	string address;

};


7.6 -7.7   对于函数add、read和print,定义你自己的版本。

#include<iostream>
#include<vector>
#include<string>
using namespace std;
struct Sales_data
{
	string isbn() const { return bookNo; }
	Sales_data& combine(const Sales_data&);
	double avg_price() const;
	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;
}
double Sales_data::avg_price() const{

	if (units_sold)
	{
		return revenue / units_sold;
	}
	else
	{
		return 0;
	}

}
istream &read(istream &is, Sales_data &item)
{
	double price=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 << " " << 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;
	if (read(cin,total))
	{
		Sales_data trans;
		while (read(cin,trans))
		{
			if (total.isbn() == trans.isbn())
			{
				total.combine(trans);
			}
			else
			{
				print(cout, total);
				total = trans;
			}

		}
		print(cout, total);
	}
	else
	{
		cerr << "NO data." << endl;
		return -1;
	}

	return 0;
}


7.8  为什么read函数将其Sales_data参数定义成普通的引用,而print函数将其参数定义成常量引用?

答:因为read函数要对形参进行写入操作,而print只需要读取操作。

7.9  对于7.1.2节(第233页)练习中的代码,添加读取和打印Person对象的操作。

struct Person
{
	string name_num() const { return name; }
	string address_num()  { return address; }//地址可变,所以不是const
	string name;
	string address;

};
istream &read(istream &is, Person &item)
{
	is >> item.name >> item.address ;
	return is;
}
ostream &print(ostream &os, Person &item)
{
	os << item.name_num() << " " << item.address_num() ;
	return os;
}


7.10  在下面这条if语句中,条件部分的作用是什么?

if (read(read(cin, data1), data2))

答: 从cin流连续读取data1、data2两个对象。


7.11-7.13  在你的Sales_data类中添加构造函数,然后编写一段程序令其用到每个构造函数。

#include<iostream>
#include<vector>
#include<string>
using namespace std;

struct Sales_data;
istream &read(istream &is, Sales_data &item);
struct Sales_data
{
	Sales_data() = default;
	Sales_data(const string &s):bookNo(s){}
	Sales_data(const string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(p){}//类内构造函数

	//Sales_data(istream &);//类外构造函数
	Sales_data(istream& is)
	{
		read(is, *this);
	}

	string isbn() const { return bookNo; }
	Sales_data& combine(const Sales_data&);
	double avg_price() const;
	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;
}
double Sales_data::avg_price() const{

	if (units_sold)
	{
		return revenue / units_sold;
	}
	else
	{
		return 0;
	}

}
istream &read(istream &is, Sales_data &item)
{
	double price=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 << " " << 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;
}
/*Sales_data::Sales_data(istream &is)
{
	read(is, *this);
}*/


int  main()
{
	Sales_data total(cin);
	//Sales_data("00");构造函数2
	//Sales_data("00", 2, 12);构造函数3
	//Sales_data(cin);

	if (!total.isbn().empty())
	{
		Sales_data trans;
		while (read(cin,trans))
		{
			if (total.isbn() == trans.isbn())
			{
				total.combine(trans);
			}
			else
			{
				print(cout, total);
				total = trans;
			}

		}
		print(cout, total);
	}
	else
	{
		cerr << "NO data." << endl;
		return -1;
	}
//	Sales_data data1, data2;
//	if (read(read(cin, data1), data2));
	return 0;
}


7.14   编写一个构造函数,令其用我们提供的类内初始值显式地初始化成员。

Sales_data() : units_sold(0) , revenue(0.0) { }

7.15  为你的 Person 类添加正确的构造函数。

struct Person;
istream &read(istream &is, Person &item);
struct Person
{
	Person() = default;
	Person(const string &s,string &s1) :name(s),address(s1){}
	
	//Sales_data(istream &);//类外构造函数
	Person(istream& is1)
	{
		read(is1, *this);
		//read(is2,address);
	}
	string name_num() const { return name; }
	string address_num()  { return address; }//地址可变,所以不是const
	string name;
	string address;

};
istream &read(istream &is, Person &item)
{
	is >> item.name >> item.address;
	return is;
}
ostream &print(ostream &os, Person &item)
{
	os << item.name_num() << " " << item.address_num();
	return os;
}

7.16  在类的定义中对于访问说明符出现的位置和次数有限定吗?如果有,是什么?什么样的成员应该定义在public说明符之后?什么样的成员应该定义在private说明符之后?

答:没有限制。对于那些属于类的接口的部分,应当出现在public后面,而那些具体实现或者数据成员则应该在private后面。


7.17  使用class 和 struct 时有区别吗?如果有,是什么?

答:有,在没有访问说明符时,默认的访问权限不同。如果我们使用struct关键字,则定义在第一个访问说明符之前的成员是public的;如果使用class关键字,则这些成员是private。

7.18  封装是何含义?它有什么用处?

答:封装是把过程和数据包围起来,对数据的访问只能通过已定义的接口。

7.19  在你的Person 类中,你将把哪些成员声明成public的?哪些声明成private的?解释你这样做的原因。

答:将数据成员声明成private的,函数成员声明成public的。因为Person类不需要直接提供数据操作给外部,只需要相应的功能接口。

7.20  友元在什么时候有用?请分别列举出使用友元的利弊。

答:在需要直接访问类的private部分的内容时有用,友元有时候能够避免开销,但也破坏了封装。

7.21  修改你的Sales_data类使其隐藏实现的细节。你之前编写的关于Sales_data操作的程序应该继续使用,借助类的新定义重新编译该程序,确保其正常工作。

#include<iostream>
#include<vector>
#include<string>
using namespace std;

struct Sales_data;
istream &read(istream &is, Sales_data &item);
struct Sales_data
{
	friend Sales_data add(const Sales_data &, const Sales_data&);//对非成员函数的友元声明
	friend istream &read(istream&, Sales_data&);
	friend ostream &print(ostream&, const Sales_data&);

public:
	Sales_data() = default;
	Sales_data(const string &s) :bookNo(s){}
	Sales_data(const string &s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(p){}//类内构造函数

	//Sales_data(istream &);//类外构造函数
	Sales_data(istream& is)
	{
		read(is, *this);
	}

	string isbn() const { return bookNo; }
	Sales_data& combine(const Sales_data&);
	double avg_price() const;

private:
	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;
}
double Sales_data::avg_price() const{

	if (units_sold)
	{
		return revenue / units_sold;
	}
	else
	{
		return 0;
	}

}
istream &read(istream &is, Sales_data &item)
{
	double price = 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 << " " << 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;
}
/*Sales_data::Sales_data(istream &is)
{
read(is, *this);
}*/


int  main()
{
	Sales_data total(cin);
	//Sales_data("00");构造函数2
	//Sales_data("00", 2, 12);构造函数3
	//Sales_data(cin);

	if (!total.isbn().empty())
	{
		Sales_data trans;
		while (read(cin, trans))
		{
			if (total.isbn() == trans.isbn())
			{
				total.combine(trans);
			}
			else
			{
				print(cout, total);
				total = trans;
			}

		}
		print(cout, total);
	}
	else
	{
		cerr << "NO data." << endl;
		return -1;
	}
	//	Sales_data data1, data2;
	//	if (read(read(cin, data1), data2));
	return 0;
}


7.22    修改你的Person类使其隐藏实现的细节。

struct Person;
istream &read(istream &is, Person &item);
ostream &print(ostream &os, Person &item)
struct Person{
   friend istream &read(istream &is, Person &item);
   friend ostream &print(ostream &os, Person &item);
public:Person() = default;Person(const string &s,string &s1) :name(s),address(s1){}//Sales_data(istream &);//类外构造函数Person(istream& is1){read(is1, *this);//read(is2,address);}string name_num() const { return name; }string address_num() { return address; }//地址可变,所以不是constprivate:
	string name;
	string address;

};
istream &read(istream &is, Person &item)
{
	is >> item.name >> item.address;
	return is;
}
ostream &print(ostream &os, Person &item)
{
	os << item.name_num() << " " << item.address_num();
	return os;

7.23   编写你自己的Screen类。

class Screen{
public:
	typedef string::size_type pos;
	//using pos = string::size_type;使用类型别名

	Screen() = default;//显示写出默认构造函数

	Screen(pos ht, pos wd, char c) :height(ht), width(wd), contents(ht*wd, c){}
	char get() const { return contents[cursor]; }//定义在类内部的成员函数是自动inline的

	inline char get(pos ht, pos wd) const;//声明处,显示内联
	Screen &move(pos rr, pos c);

	void some_member() const;
private:
	pos cursor = 0;
	pos height = 0, width = 0;
	string contents;
	mutable size_t access_ctr;
};
inline 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];
}

void Screen::some_member() const{ ++access_ctr; }


7.24  给你的Screen类添加三个构造函数:一个默认构造函数;另一个构造函数接受宽和高的值,然后将contents初始化成给定数量的空白;第三个构造函数接受宽和高的值以及一个字符,该字符作为初始化后屏幕的内容。

Screen() = default;
Screen(unsign x, unsign y): h(x), w(y), contents(10, ' ')
{

}
Screen(unsign h_p, unsign w_p, char c): h(h_p), w(w_p), contents(1, c)
{

}


7.25  Screen 能安全地依赖于拷贝和赋值操作的默认版本吗?如果能,为什么?如果不能?为什么?

答:可以依赖,因为类不需要额外分配对象之外的资源,且string类在合成的构造函数中能正常工作。


7.26  将Sales_data::avg_price 定义成内联函数。

inline Sales_data::avg_price() {  }

7.27  给你自己的Screen 类添加move、set 和display 函数,通过执行下面的代码检验你的类是否正确。

#include<iostream>
#include<vector>
#include<string>
using namespace std;
class Screen{
public:
	typedef string::size_type pos;
	//using pos = string::size_type;使用类型别名

	Screen() = default;//显示写出默认构造函数

	Screen(pos ht, pos wd, char c) :height(ht), width(wd), contents(ht*wd, c){}
	char get() const { return contents[cursor]; }//定义在类内部的成员函数是自动inline的

	inline char get(pos ht, pos wd) const;//声明处,显示内联
	Screen &move(pos rr, pos c);

	void some_member() const;

	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;
	mutable size_t access_ctr;
	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;
}
char Screen::get(pos r, pos c) const
{
	pos row = r*width;
	return contents[row + c];
}

void Screen::some_member() const{ ++access_ctr; }

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;
}
/*(class Window_mgr{

private:
	vector<Screen> screens{ Screen(24, 80, ' ') };//提供一个类内初始值时,必须以“=”或者{}表示
};*/
int main()
{
	Screen myScreen(5, 5, 'X');
	myScreen.move(4, 0).set('#').display(cout);
	cout << "\n";
	myScreen.display(cout);
	cout << "\n";
	

	return 0;
}


7.28  如果move、set和display函数的返回类型不是Screen&而是Screen,则在上一个练习中将会发生什么?

答:对myScreen对象的操作只能成功一次,链式表达式之后的都无法成功。

7.29  修改你的Screen 类,令move、set和display函数返回Screen并检查程序的运行结果,在上一个练习中你的推测正确吗?

正确

7.30  通过this指针使用成员的做法虽然合法,但是有点多余。讨论显示地使用指针访问成员的优缺点。

并没有感觉到有什么优缺点...

7.31  定义一对类X和Y,其中X 包含一个指向 Y 的指针,而Y包含一个类型为 X 的对象。

class Y;

class X
{
    Y *py;
};

class Y
{
    X x;
};

7.32  定义你自己的Screen 和 Window_mgr,其中clear是Window_mgr的成员,是Screen的友元。

class Screen
{
public:
    Screen() = default;
    Screen(int a, int b, string st): i(a), j(b), s(st) { }
    friend class Window_mgr;

private:
    int i = 0, j = 0;
    string s;
};

class Window_mgr
{
public:
    Window_mgr();
    void clear(size_t);

private:
    vector<Screen> v{Screen(1, 2, "3")};
};

void Window_mgr::clear(size_t i)
{
    v[i].s = string(v[i].i * v[i].j, ' ');
}

7.33   如果我们给Screen 添加一个如下所示的size成员将发生什么情况?如果出现了问题,请尝试修改它。

pos Screen::size() const
{
    return height * width;
}
答:放在类内且定义pos之后。类外将出现错误。

7.34  如果我们把第256页Screen类的pos的typedef放在类的最后一行会发生什么情况?

答:将会找不到类型pos,导致编译错误。

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;
}
复制代码
复制代码
typedef string Type;                //定义string类型别名Type
Type initVal();                     //全局函数声明,返回类型为Type(string)

class Exercise
{
public:
    typedef double Type;            //定义double类型别名Type
    Type setVal(Type);              //成员函数声明,返回类型、形参为Type(double)
    Type initVal();                 //成员函数声明,返回类型为Type,函数名隐藏全局作用域的initVal()
private:
    int val;
};

Type Exercise::setVal(Type parm)    //类外定义成员函数,返回类型、形参为Type(double)
{
    val = parm + initVal();         //initVal()为类Exercise()的成员函数
    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(i % j)
    {

    }

    int rem, base;
};


7.37   使用本节提供的Sales_data类,确定初始化下面的变量时分别使用了哪个构造函数,然后罗列出每个对象所有的数据成员的值。

Sales_data first_item(cin);            //Sales_data(std::istream &is)    用户输入的值

int main()
{
    Sales_data next;                //Sales_data(std::string s = "") bookNo="",units_sold=0,revenue=0.0 默认构造函数
    Sales_data last("9-999-99999-9");    //Sales_data(std::string s = "") bookNo="9-999-99999-9",units_sold=0,revenue=0.0
}

7.38   有些情况下我们希望提供cin作为接受istream& 参数的构造函数的默认实参,请声明这样的构造函数。

Sales_data(std::istream &is = cin);

7.39  如果接受string的构造函数和接受 istream&的构造函数都使用默认实参,这种行为合法吗?如果不,为什么?

答:不合法,因为这样无法确定具体调用哪个构造函数。

7.40  从下面的抽象概念中选择一个(或者你自己指定一个),思考这样的类需要哪些数据成员,提供一组合理的构造函数并阐明这样做的原因。

(a) Book           (b) Data           (c) Employee
(d) Vehicle        (e) Object        (f) Tree
对于(a)Book来说,数据成员要有书名,书的数量,书的价格,书的刊号,书的作者,出版日期等。

class Book
{
public:
    Book();
    Book(string n1, string n2, double p1, string a1, string d1): bookName(n1), book_num(n2), book_price(p1), book_author(a1), book_date(d1) { }

private:
    string bookName;
    string book_num;
    double book_price;
    string book_isbn;
    string book_author;
    string book_date;
};

7.41  使用委托构造函数重新编写你的Sales_data 类,给每个构造函数体添加一条语句,令其一旦执行就打印一条信息。用各种可能的方式分别创建 Sales_data 对象,认真研究每次输出的信息直到你确实理解了委托构造函数的执行顺序。

#include <iostream>
#include<string>
using namespace std;

class Sales_data;
istream& read(istream& in, Sales_data& s);

class Sales_data
{
    friend istream& read(istream& in, Sales_data& s);
public:
    Sales_data(): Sales_data("", 0, 0.0)
    {
        cout << "default constructor.\n";
    }

    Sales_data(istream& is): Sales_data("", 0, 0.0)
    {
        cout << "istream constructor.\n";
        read(is, *this);
    }

    Sales_data(string s, unsigned u, double d)
    {
        cout << "3 parameter constructor.\n";
    }

private:
    string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;

};

istream& read(istream& in, Sales_data& s)
{
    in >> s.bookNo >> s.units_sold >> s.revenue;
    return in;
}

int main(int argc, char const * argv[])
{
    Sales_data s1;
    Sales_data s2(cin);
    return 0;
}

7.42  对于你在练习7.40(参见7.5.1节,第261页)中编写的类,确定哪些构造函数可以使用委托。如果可以的话,编写委托构造函数。如果不可以,从抽象概念列表中重新选择一个你认为可以使用委托构造函数的,为挑选出的这个概念编写类定义。

#include <iostream>
#include<string>
using namespace std;

class Book
{
public:
    Book();
    Book(string n1, string n2, double p1, string a1, string d1): bookName(n1), book_num(n2), book_price(p1), book_author(a1), book_date(d1)
    {
        cout << "6 parameter constructor.\n";
    }

private:
    string bookName;
    string book_num;
    double book_price;
    string book_isbn;
    string book_author;
    string book_date;
};

Book::Book(): Book("", "", 0.0, "", "")
{
    cout << "default constructor.\n";
}

int main(int argc, char const * argv[])
{
    Book b1;
    return 0;
}

7.43 假定有一个名为 NoDefault 的类,它有一个接受 int 的构造函数,但是没有默认构造函数。定义类 C,C 有一个 NoDefault 类型的成员,定义C 的默认构造函数。

#include <iostream>

using namespace std;

class NoDefault
{
public:
    NoDefault(int i);

private:
    int n;
};

NoDefault::NoDefault(int i): n(i)
{
    cout << "NoDefault constructor.\n";
}

class C
{
public:
    C();

private:
    NoDefault cN;
};

C::C(): cN(0)
{
cout << "c default constructor.\n";
}int main(int argc, char const * argv[]){ C c1; return 0;}


7.44  下面这条声明合法吗?如果不,为什么?

vector<NoDefault> vec(10);

答:不合法,因为NoDefault既没有默认构造函数又不能值初始化。

7.45  如果在上一个练习中定义的vector的元素类型是C,则声明合法吗?为什么?

答:合法,因为C有默认构造函数。

7.46  下面哪些论断是不正确的?为什么?

(a) 一个类必须至少提供一个构造函数。
(b) 默认构造函数是参数列表为空的构造函数。
(c) 如果对于类来说不存在有意义的默认值,则类不应该提供默认构造函数。
(d) 如果类没有定义默认构造函数,则编译器将为其生成一个并把每个数据成员初始化成相应类型的默认值。

(a)不正确,简单的类可以依靠编译器自动合成
(b)正确
(c)正确,<<More Effective C++>>中说到:非必要不提供default constructor,添加无意义的default constructor(提供了无意义的默认值)会影响效率,因为成员函数必须测试字段是否被初始化。
(d)当类没有定义任何构造函数时,才会自动合成构造函数。

7.47  说明接受一个string 参数的Sales_data构造函数是否应该是explicit的,并解释这样做的优缺点。

答:是否需要从string到Sales_data的转换依赖于我们对用户使用该转换的看法,也就是说要视具体情况而定。

7.48  假定Sales_data 的构造函数不是explicit的,则下述定义将执行什么样的操作?

string null_isbn("9-999-9999-9");
Sales_data item1(null_isbn);
Sales_data item2("9-999-99999-9");

如果不是explicit的:
string null_isbn("9-999-99999-9");//用字符字面值初始化null_isbn
Sales_data item1(null_isbn); //定义了一个Sales_data对象,该对象使用null_isbn转换得到的临时对象进行初始化
Sales_data item2("9-999-99999-9"); //定义了一个Sales_data对象,该对象使用字符串字面值转换得到的临时对象进行初始化
如果是explicit的:
string null_isbn("9-999-99999-9");
Sales_data item1(null_isbn); //定义了一个Sales_data对象,该对象使用null_isbn转换得到的临时对象进行初始化
Sales_data item2("9-999-99999-9"); //定义了一个Sales_data对象,该对象使用字符串字面值转换得到的临时对象进行初始化
构造函数是否是explicit的与直接显式定义对象调用特定的构造函数无关,explicit用于在需要一个从一种类型隐式转换到另一种类型时进行转换抑制。

7.49  对于combine 函数的三种不同声明,当我们调用i.combine(s) 时分别发生什么情况?其中 i 是一个 Sales_data,而 s 是一个string对象。

(a) Sales_data &combine(Sales_data);    //正确,string隐式转换到Sales_data
(b) Sales_data &combine(Sales_data&);   //错误,不能从string转换到Sales_data &
(c) Sales_data &combine(const Sales_data&) const;        //正确,string隐式转换为一个临时Sales_data,该Sales_data被绑定到const Sales_data &

7.50  确定在你的Person类中是否有一些构造函数应该是 explicit 的。

答:是否需要explicit是根据具体情况而定的。

7.51  vector 将其单参数的构造函数定义成 explicit 的,而string则不是,你觉得原因何在?

答:如果vector单参数构造函数不是explicit的,那么对于这样的一个函数void fun(vector<int> v)来说,可以直接以这样的形式进行调用fun(5),这种调用容易引起歧义,无法得知实参5指的是vector的元素个数还是只有一个值为5的元素。而string类型不是一个容器,不存在这样的歧义问题。

7.52  使用2.6.1节(第64页)的 Sales_data 类,解释下面的初始化过程。如果存在问题,尝试修改它。

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

答:将Sales_data的bookNo成员初始化为"978-0590353403",将units_sold初始化为25,将revenue初始化为15.99

7.53  定义你自己的 Debug。

复制代码
class Debug
{
public:
    constexpr Debug(int ii, double dd): i(ii), d(dd)
    {

    }
    ~Debug() = default;//析构函数的默认定义

private:
    int i;
    double d;
};
复制代码

7.54  Debug中以 set_ 开头的成员应该被声明成 constexpr 吗?如果不,为什么?

答:不应该,因为set_开头的成员不含有return 语句。(constexpr函数的要求:拥有唯一的可执行语句是返回语句。)

7.55   7.5.5节(第266页)的 Data 类是字面值常量类吗?请解释原因。

答:不是,因为数据成员s是string类型,而string类型不是字面值类型。

7.56  

什么是类的静态成员?它有何优点?静态成员与普通成员有何区别?

在类中使用static关键词修饰的成员。优点是能适用于某些普通成员无法使用的场合。静态成员与普通成员的区别是静态成员属于整个类,而不是属于一个对象,也即一个类只有一份静态成员,而有多份对象。

7.57   

编写你自己的 Account 类。

复制代码
class Account
{
public:
    int i;
    static char c;

};

char Account::c = 'A';
复制代码

7.58  下面的静态数据成员的声明和定义有错误吗?请解释原因。

复制代码
// example.h
class Example
{
public:
    static double rate = 6.5;                  //错误,非constexpr static数据成员不允许在类内初始化
    static const int vecSize = 20;             //正确

    //另外需要防止下面的声明被解析成函数成员的声明
    static vector<double> vec(vecSize);        //错误,constexpr static数据成员必须是字面值类型,vector非字面值类型,不允许类内初始化。该语句对vec初始化20个0.
};
// example.C
#include "example.h"
double Example::rate;//改:double Example::rate=6.5;

vector<double> Example::vec;//改:vector<double> Example::vec(vecSize);

复制代码








































































































  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值