《C++ Primer》第7章 类
7.5节 构造函数再探
#include <iostream>
#include <string>
#include <vector>
using namespace std;
//该类型没有显式定义默认构造函数,编译器也不会为它合成一个
class NoDefault
{
public:
NoDefault(int i)
{
val = i;
cout << "NoDefault构造函数===========" << endl;
}
int val;
};
class C
{
public:
NoDefault nd;
//必须显式调用Nodefault的带参构造函数初始化nd
C(int i = 0): nd(i)
{
cout << "C构造函数===========" << endl;
}
};
int main()
{
C c; //使用了类型C的默认构造函数
cout << c.nd.val << endl;
vector<C> vec(10);
return 0;
}
运行结果:
练习7.47:说明接受一个string参数的Sales_data构造函数是否应该是explicit的,并解释这样做的优缺点。
【出题思路】
explicit用于抑制类类型的隐式转换,读者需要知道explicit的长处和不足。
【解答】
接受一个string参数的Sales_data构造函数应该是explicit的,否则,编译器就有可能自动把一个string对象转换成Sales_data对象,这种做法显得有些随意,某些时候会与程序员的初衷相违背。使用explicit的优点是避免因隐式类类型转换而带来意想不到的错误,缺点是当用户的确需要这样的类类型转换时,不得不使用略显烦琐的方式来实现。
练习7.48:假定Sales_data的构造函数不是explicit的,则下述定义将执行什么样的操作?
string null_isbn("9-999-99999-9");
Sales_data item1(null_isbn);
Sales_data item2("9-999-99999-9");
如果Sales_data的构造函数是explicit的,又会发生什么呢?
【出题思路】
构造函数如果不是explicit的,则string对象隐式地转换成Sales_data对象;相反,构造函数如果是explicit的,则隐式类类型转换不会发生。
【解答】
在本题给出的代码中,第一行创建了一个string对象,第二行和第三行都是调用Sales_data的构造函数(该构造函数接受一个string)创建它的对象。此处无须任何类类型转换,所以不论Sales_data的构造函数是不是explicit的,item1和item2都能被正确地创建,它们的bookNo成员都是9-999-99999-9,其他成员都是0。
#include <iostream>
#include <string>
using namespace std;
class Person
{
friend std::istream& operator >> (std::istream&, Person&);
private:
string strName;
string strAddress;
public:
Person() = default;
Person(const string &name, const string &add)
{
strName = name;
strAddress = add;
}
explicit Person(std::istream &input) { input >> *this; }
string getName() const
{
return strName;
}
string getAddress() const
{
return strAddress;
}
};
std::istream& operator >> (std::istream& in, Person& per)
{
in >> per.strName >> per.strAddress;
return in;
}
int main()
{
cout << "请输入姓名和地址:" << endl;
Person person(cin);
cout << "name: " << person.getName() << " address: " << person.getAddress() << endl;
return 0;
}
运行结果:
struct Sales_data
{
string bookNo;
unsigned units_sold;
double revenue;
};
#include <iostream>
#include <string>
using namespace std;
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;
};
int main()
{
constexpr Debug io_sub(false, true, false); //调式IO
if(io_sub.any()) //等价于if(true)
{
cerr << "print appropriate error messags" << endl;
}
constexpr Debug prod(false); //无调式
if(prod.any()) //等价于if(false)
{
cerr << "print an error message" << endl;
}
return 0;
}
运行结果:
练习7.54:Debug中以set_开头的成员应该被声明成constexpr吗?如果不,为什么?
【出题思路】
理解constexpr函数的用法。
【解答】
这些以set_开头的成员不能声明成constexpr,这些函数的作用是设置数据成员的值,而constexpr函数只能包含return语句,不允许执行其他任务。