摘要:构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。构造函数名字和类名相同,没有返回类型。构造函数放在类的部分要求编译器合成默认的构造函数。练习在你的类中添加构造函数,然后编写一段程序令其用到每个构造函数。
std::string isbn() const {return bookNo};
↓
伪代码:说明隐式的this
指针是如何使用的
下面的代码是非法的:因为我们不能显示地定义自己的this
指针, 在Sales_data成员函数中,this的类型是Sales_data *const
因为isbn是一个常量成员,此处的this是一个指向常量的指针
std::string Sales_data::isbn(const Sales_data *const this){ return this->isbn;}
.
来访问成员函数total.isbn()
当isbn返回bookNo时,实际上它隐式的返回total.bookNo。成员函数通过一个名为this
的额外的隐式参数来访问调用它的那个对象。this
伪代码:Sales_data::isbn(&total)
this
,尽管没有必要,但是能把isbn
定义成:std::string isbn() const {return this->bookNo}
const
作用:修改隐式this
指针的类型。紧跟在参数列表后面的const
表示this
是一个指向常量的指针 —— 使用const
的成员函数被称为 常量成员函数。 const
成员函数:this
是指向const类类型的const
指针(既不能改变this
所指向的值,也不能改变this
保存的地址)。const
成员函数:this
是指向类类型的const
指针(可以改变this
所指向的值,不能改变this
保存的地址)。和类相关的非成员函数,定义和声明都应该在类的外部。
一般来说,如果非成员函数是类接口的组成部分,则这些函数的声明应该与类在同一个头文件中。
public
部分= default
要求编译器合成默认的构造函数。(C++11)Sales_item(): units_sold(0), revenue(0.0) { }
、public
:定义在public
后面的成员在整个程序内可以被访问;public
成员定义类的接口private
:定义在private
后面的成员可以被类的成员函数访问,但不能被使用该类的代码访问;private
隐藏了类的实现细节。class
和struct
:都可以被用于定义一个类,唯一的区别在于默认访问权限。 class
:在第一个访问说明符之前的成员是private
的。struct
:在第一个访问说明符之前的成员是public
的。类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元。如果类想把一个函数作为他的友元,只需要增加一条以friend
关键字开始的函数声明语句即可。
friend
开始, friend Sales_data add(const Sales_data&, const Sales_data&);
表示非成员函数add
可以访问类的非公有成员。inline
inline
。mutable
关键字:mutable data access_ctr;
const
,即使他是const
对象的成员const
或者是引用的话,必须将其初始化。只能初始化,不能赋值(注意初始化和赋值的区别) Sales_data(): Sales_data(" ", 0, 0) { }
只有当一个类没有定义 任何构造函数的时候,编译器才会生成一个默认构造函数。
如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制。这种构造函数又叫转换构造函数(converting constructor)。
能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则。
只允许 一步类类型转换
抑制构造函数定义的隐式转换
explicit
加以阻止explicit
关键字只允许出现在类内的构造函数声明处(只对一个实参的构造函数有效)explicit
构造函数只能用于直接初始化,不能将explicit
构造函数用于拷贝形式的初始化过程。Sales_data item1(null_book); //正确,直接初始化Sales_data item2 = null_book; //错误:不能将 explicit 构造函数用于拷贝形式的初始化过程
explicit
的构造函数用于隐式转换过程,但是我们可以使用这样的构造函数 显式地强制进行转换。聚合类使得用户可以直接访问其成员,并且具有特殊的初始化语法形式。
满足以下所有条件:
public
的virtual
函数可以使用一个花括号括起来的成员初始化列表,并用它初始化聚合类的数据成员。初始值的顺序必须与声明的顺序一致。
constexpr
函数的参数和返回值必须是字面值类型constexpr
构造函数。constexpr
构造函数。static
数据成员存在于类类型的每个对象中。static
数据成员独立于该类的任意对象而存在。static
数据成员是与类关联的对象,并不与该类的对象相关联。static
::
直接访问静态成员 r = Account::rate()
r = ac1.rate()
使用指针访问:r = ac2->rate()
static
关键字,该关键字只出现在类内部的声明语句。static
。double Account::interestRate = initRate();
constexpr
。使用2.6.1节定义的Sales_data
类为1.6节的交易处理程序编写一个新版本。
解:
#include #include using std::cin; using std::cout; using std::endl; using std::string;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; total.revenue += trans.revenue; } else { cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl; total = trans; } } cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl; } else { std::cerr << "No data?!" << std::endl; return -1; } return 0;}
曾在2.6.2节的练习中编写了一个Sales_data
类,请向这个类添加combine
函数和isbn
成员。
解:
#include struct Sales_data { std::string isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&); std::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;}
修改7.1.1节的交易处理程序,令其使用这些成员。
解:
#include using std::cin; using std::cout; using std::endl;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 { std::cerr << "No data?!" << std::endl; return -1; } return 0;}
编写一个名为Person
的类,使其表示人员的姓名和地址。使用string
对象存放这些元素,接下来的练习将不断充实这个类的其他特征。
解:
#include class Person { std::string name; std::string address;};
在你的Person
类中提供一些操作使其能够返回姓名和地址。 这些函数是否应该是const
的呢?解释原因。
解:
#include class Person { std::string name; std::string address;public: auto get_name() const -> std::string const& { return name; } auto get_addr() const -> std::string const& { return address; }};
应该是const
的。因为常量的Person
对象也需要使用这些函数操作。
对于函数add
、read
和print
,定义你自己的版本。
解:
#include #include struct Sales_data { std::string const& isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&); std::string bookNo; unsigned units_sold = 0; double revenue = 0.0;};// member functions.Sales_data& Sales_data::combine(const Sales_data& rhs){ units_sold += rhs.units_sold; revenue += rhs.revenue; return *this;}// nonmember functionsstd::istream &read(std::istream &is, Sales_data &item){ double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; 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;}
使用这些新函数重写7.1.2节练习中的程序。
int main(){ Sales_data total; if (read(std::cin, total)) { Sales_data trans; while (read(std::cin, trans)) { if (total.isbn() == trans.isbn()) total.combine(trans); else { print(std::cout, total) << std::endl; total = trans; } } print(std::cout, total) << std::endl; } else { std::cerr << "No data?!" << std::endl; return -1; } return 0;}
为什么read
函数将其Sales_data
参数定义成普通的引用,而print
函数将其参数定义成常量引用?
解:
因为read
函数会改变对象的内容,而print
函数不会。
对于7.1.2节练习中代码,添加读取和打印Person
对象的操作。
#include #include struct Person { std::string const& getName() const { return name; } std::string const& getAddress() const { return address; } std::string name; std::string address;};std::istream &read(std::istream &is, Person &person){ return is >> person.name >> person.address;}std::ostream &print(std::ostream &os, const Person &person){ return os << person.name << " " << person.address;}
在下面这条if
语句中,条件部分的作用是什么?
if (read(read(cin, data1), data2)) //等价read(std::cin, data1);read(std::cin, data2);
解:
read
函数的返回值是istream
对象, if
语句中条件部分的作用是从输入流中读取数据给两个data
对象。
在你的Sales_data
类中添加构造函数, 然后编写一段程序令其用到每个构造函数。
解:
头文件
#include #include struct Sales_data { Sales_data() = default; Sales_data(const std::string &s):bookNo(s) { } Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ } Sales_data(std::istream &is); std::string isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&); std::string bookNo; unsigned units_sold = 0; double revenue = 0.0;};// nonmember functionsstd::istream &read(std::istream &is, Sales_data &item){ double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; 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;}// member functions.Sales_data::Sales_data(std::istream &is){ read(is, *this);}Sales_data& Sales_data::combine(const Sales_data& rhs){ units_sold += rhs.units_sold; revenue += rhs.revenue; return *this;}
主函数
int main(){ Sales_data item1; print(std::cout, item1) << std::endl; Sales_data item2("0-201-78345-X"); print(std::cout, item2) << std::endl; Sales_data item3("0-201-78345-X", 3, 20.00); print(std::cout, item3) << std::endl; Sales_data item4(std::cin); print(std::cout, item4) << std::endl; return 0;}
把只接受一个istream
作为参数的构造函数移到类的内部。
解:
#include #include 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 n, double p):bookNo(s), units_sold(n), revenue(n*p){ } Sales_data(std::istream &is) { read(is, *this); } std::string isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&); std::string bookNo; unsigned units_sold = 0; double revenue = 0.0;};// member functions.Sales_data& Sales_data::combine(const Sales_data& rhs){ units_sold += rhs.units_sold; revenue += rhs.revenue; return *this;}// nonmember functionsstd::istream &read(std::istream &is, Sales_data &item){ double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; 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;}
使用istream
构造函数重写第229页的程序。
int main(){ Sales_data total(std::cin); if (!total.isbn().empty()) { std::istream &is = std::cin; while (is) { Sales_data trans(is); if (!is) break; if (total.isbn() == trans.isbn()) total.combine(trans); else { print(std::cout, total) << std::endl; total = trans; } } print(std::cout, total) << std::endl; } else { std::cerr << "No data?!" << std::endl; return -1; } return 0;}
编写一个构造函数,令其用我们提供的类内初始值显式地初始化成员。
Sales_data() : units_sold(0) , revenue(0) { }
为你的Person
类添加正确的构造函数。
#include #include struct Person;std::istream &read(std::istream&, Person&);struct Person{ Person() = default; Person(const std::string& sname, const std::string& saddr) :name(sname), address(saddr) {} Person(std::istream &is) { read(is, *this); } std::string getName() const { return name; } std::string getAddress() const { return address; } std::string name; std::string address;};std::istream &read(std::istream &is, Person &person){ is >> person.name >> person.address; return is;}std::ostream &print(std::ostream &os, const Person &person){ os << person.name << " " << person.address; return os;}
在类的定义中对于访问说明符出现的位置和次数有限定吗? 如果有,是什么?什么样的成员应该定义在public
说明符之后? 什么样的成员应该定义在private
说明符之后?
解:
在类的定义中对于访问说明符出现的位置和次数没有限定。
每个访问说明符指定了接下来的成员的访问级别,其有效范围直到出现下一个访问说明符或者达到类的结尾处为止。
如果某个成员能够在整个程序内都被访问,那么它应该定义为public
; 如果某个成员只能在类内部访问,那么它应该定义为private
。
使用class
和struct
时有区别吗?如果有,是什么?
解:
class
和struct
的唯一区别是默认的访问级别不同。
封装是何含义?它有什么用处?
将类内部分成员设置为外部不可见,而提供部分接口给外面,这样的行为叫做封装。
用处:
在你的Person
类中,你将把哪些成员声明成public
的? 哪些声明成private
的? 解释你这样做的原因。
构造函数、getName()
、getAddress()
函数将设为public
。 name
和 address
将设为private
。 函数是暴露给外部的接口,因此要设为public
; 而数据则应该隐藏让外部不可见。
修改你的Sales_data
类使其隐藏实现的细节。 你之前编写的关于Sales_data
操作的程序应该继续使用,借助类的新定义重新编译该程序,确保其正常工作。
#include #include class Sales_data { friend std::istream &read(std::istream &is, Sales_data &item); friend std::ostream &print(std::ostream &os, const Sales_data &item); friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs);public: Sales_data() = default; Sales_data(const std::string &s):bookNo(s) { } Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ } Sales_data(std::istream &is) { read(is, *this); } std::string isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&);private: std::string bookNo; unsigned units_sold = 0; double revenue = 0.0;};// member functions.Sales_data& Sales_data::combine(const Sales_data& rhs){ units_sold += rhs.units_sold; revenue += rhs.revenue; return *this;}// friend functionsstd::istream &read(std::istream &is, Sales_data &item){ double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; 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;}
修改你的Person
类使其隐藏实现的细节。
#include #include class Person { friend std::istream &read(std::istream &is, Person &person); friend std::ostream &print(std::ostream &os, const Person &person);public: Person() = default; Person(const std::string sname, const std::string saddr):name(sname), address(saddr){ } Person(std::istream &is){ read(is, *this); } std::string getName() const { return name; } std::string getAddress() const { return address; }private: std::string name; std::string address;};std::istream &read(std::istream &is, Person &person){ is >> person.name >> person.address; return is;}std::ostream &print(std::ostream &os, const Person &person){ os << person.name << " " << person.address; return os;}
编写你自己的Screen
类型。
#include class Screen { public: using pos = std::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]; } char get(pos r, pos c) const { return contents[r*width+c]; } private: pos cursor = 0; pos height = 0, width = 0; std::string contents;};
给你的Screen
类添加三个构造函数:一个默认构造函数;另一个构造函数接受宽和高的值,然后将contents
初始化成给定数量的空白;第三个构造函数接受宽和高的值以及一个字符,该字符作为初始化后屏幕的内容。
#include class Screen { public: using pos = std::string::size_type; Screen() = default; // 1 Screen(pos ht, pos wd):height(ht), width(wd), contents(ht*wd, " "){ } // 2 Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c){ } // 3 char get() const { return contents[cursor]; } char get(pos r, pos c) const { return contents[r*width+c]; } private: pos cursor = 0; pos height = 0, width = 0; std::string contents;};
给你自己的Screen
类添加move
、set
和display
函数,通过执行下面的代码检验你的类是否正确。
Screen myScreen(5, 5, "X");myScreen.move(4, 0).set("#").display(cout);cout << "/n";myScreen.display(cout);cout << "/n";
解:
增加代码:
#include #include class Screen {public: ... ... inline Screen& move(pos r, pos c); inline Screen& set(char c); inline Screen& set(pos r, pos c, char ch); const Screen& display(std::ostream &os) const { do_display(os); return *this; } Screen& display(std::ostream &os) { do_display(os); return *this; }private: void do_display(std::ostream &os) const { os << contents; } ... ...};inline Screen& Screen::move(pos r, pos c){ cursor = r*width + c; return *this;}inline Screen& Screen::set(char c){ contents[cursor] = c; return *this;}inline Screen& Screen::set(pos r, pos c, char ch){ contents[r*width+c] = ch; 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;}
如果move
、set
和display
函数的返回类型不是Screen&
而是Screen
,则在上一个练习中将会发生什么?
解:
如果返回类型是Screen
,那么move
返回的是*this
的一个副本,因此set
函数只能改变临时副本而不能改变myScreen
的值。
修改你的Screen
类,令move
、set
和display
函数返回Screen
并检查程序的运行结果,在上一个练习中你的推测正确吗?
解:
推测正确。
#with "&"XXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXX#XXXX ^# without "&"XXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXX ^
定义一对类X
和Y
,其中X
包含一个指向Y
的指针,而Y
包含一个类型为X
的对象。
class Y;class X { Y* y = nullptr;};class Y { X x;};
定义你自己的Screen
和Window_mgr
,其中clear
是Window_mgr
的成员,是Screen
的友元。
#include #include #include class Screen;class Window_mgr {public: using ScreenIndex = std::vector::size_type; //按照编号将指定的Screen置为空白 void clear(ScreenIndex); private: std::vector screen;};class Screen { friend void Window_mgr::clear(ScreenIndex);public: using pos = std::string::size_type; //构造函数 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){ } inline Screen& move(pos r, pos c); inline Screen& set(char c); inline Screen& set(pos r, pos c, char ch); const Screen& display(std::ostream& os) const { do_display(os); return *this; } Screen& display(std::ostream& os) { do_display(os); return *this; }private: pos cursor = 0; pos height = 0, width = 0; std::string contents; void do_display(std::ostream& os) const { os << contents; }};inline void Window_mgr::clear(ScreenIndex i){ Screen& s = screen[i]; s.contents = std::string(s.height * s.width, " ");}inline Screen& Screen::move(pos r, pos c){ cursor = r * width + c; return *this;}inline Screen& Screen::set(char c){ contents[cursor] = c; return *this;}inline Screen& Screen::set(pos r, pos c, char ch){ contents[r * width + c] = ch; return *this;}
如果我们把第256页Screen
类的pos
的typedef
放在类的最后一行会发生什么情况?
解:
在 dummy_fcn(pos height) 函数中会出现 未定义的标识符pos。
类型名的定义通常出现在类的开始处,这样就能确保所有使用该类型的成员都出现在类名的定义之后。
有些情况下我们希望提供cin
作为接受istream&
参数的构造函数的默认实参,请声明这样的构造函数。
解:
Sales_data(std::istream &is = std::cin) { read(is, *this); }
使用委托构造函数重新编写你的Sales_data
类,给每个构造函数体添加一条语句,令其一旦执行就打印一条信息。用各种可能的方式分别创建Sales_data
对象,认真研究每次输出的信息直到你确实理解了委托构造函数的执行顺序。
头文件
#ifndef CP5_ex7_41_h#define CP5_ex7_41_h#include #include class Sales_data { friend std::istream &read(std::istream &is, Sales_data &item); friend std::ostream &print(std::ostream &os, const Sales_data &item); friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs);public: Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p) { std::cout << "Sales_data(const std::string&, unsigned, double)" << std::endl; } Sales_data() : Sales_data("", 0, 0.0f) { std::cout << "Sales_data()" << std::endl; } Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f) { std::cout << "Sales_data(const std::string&)" << std::endl; } Sales_data(std::istream &is); std::string isbn() const { return bookNo; } Sales_data& combine(const Sales_data&); private: inline double avg_price() const; private: std::string bookNo; unsigned units_sold = 0; double revenue = 0.0;};inlinedouble Sales_data::avg_price() const{ return units_sold ? revenue/units_sold : 0;}// declarations for nonmember parts of the Sales_data interface.std::istream &read(std::istream &is, Sales_data &item);std::ostream &print(std::ostream &os, const Sales_data &item);Sales_data add(const Sales_data &lhs, const Sales_data &rhs);#endif
源文件
#include "ex_7_41.h"// constructorSales_data::Sales_data(std::istream &is) : Sales_data(){ std::cout << "Sales_data(istream &is)" << std::endl; read(is, *this);}// member functions.Sales_data& Sales_data::combine(const Sales_data& rhs){ units_sold += rhs.units_sold; revenue += rhs.revenue; return *this;}// friend functionsstd::istream &read(std::istream &is, Sales_data &item){ double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; 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;}
主函数
#include "ex_7_41.h"using std::cout; using std::endl;int main(){ cout << "1. default way: " << endl; cout << "----------------" << endl; Sales_data s1; cout << "/n2. use std::string as parameter: " << endl; cout << "----------------" << endl; Sales_data s2("CPP-Primer-5th"); cout << "/n3. complete parameters: " << endl; cout << "----------------" << endl; Sales_data s3("CPP-Primer-5th", 3, 25.8); cout << "/n4. use istream as parameter: " << endl; cout << "----------------" << endl; Sales_data s4(std::cin); return 0;}
输出
1. default way:----------------Sales_data(const string& s, unsigned n, double p)Sales_data()2. use std::string as parameter:----------------Sales_data(const string& s, unsigned n, double p)Sales_data(const string& s)3. complete parameters:----------------Sales_data(const string& s, unsigned n, double p)4. use istream as parameter:----------------Sales_data(const string& s, unsigned n, double p)Sales_data()Sales_data(istream& is)
总结:使用委托构造函数,调用顺序是:
假定有一个名为NoDefault
的类,它有一个接受int
的构造函数,但是没有默认构造函数。定义类C
,C
有一个 NoDefault
类型的成员,定义C
的默认构造函数。
class NoDefault {public: NoDefault(int i) { }};class C {public: C() : def(0) { } private: NoDefault def;};
说明接受一个string
参数的Sales_data
构造函数是否应该是explicit
的,并解释这样做的优缺点。
解:
是否需要从string
到Sales_data
的转换依赖于我们对用户使用该转换的看法。在此例中,这种转换可能是对的。null_book
中的string
可能表示了一个不存在的ISBN
编号。
优点:
可以抑制构造函数定义的隐式转换
缺点:
为了转换要显式地使用构造函数
假定Sales_data
的构造函数不是explicit
的,则下述定义将执行什么样的操作?
解:
string null_isbn("9-999-9999-9");Sales_data item1(null_isbn);Sales_data item2("9-999-99999-9");
这些定义和是不是explicit
的无关。
对于combine
函数的三种不同声明,当我们调用i.combine(s)
时分别发生什么情况?其中i
是一个Sales_data
,而s
是一个string
对象。
解:
(a) Sales_data &combine(Sales_data); // ok(b) Sales_data &combine(Sales_data&); // error C2664: 无法将参数 1 从“std::string”转换为“Sales_data &” 因为隐式转换只有一次(c) Sales_data &combine(const Sales_data&) const; // 该成员函数是const 的,意味着不能改变对象。而 combine函数的本意就是要改变对象
确定在你的Person
类中是否有一些构造函数应该是explicit
的。
explicit Person(std::istream& is) { read(is, *this); }
vector
将其单参数的构造函数定义成explicit
的,而string
则不是,你觉得原因何在?
假如我们有一个这样的函数:
int getSize(const std::vector&);
如果vector
没有将单参数构造函数定义成explicit
的,我们就可以这样调用:
getSize(34);
很明显这样调用会让人困惑,函数实际上会初始化一个拥有34个元素的vecto
r的临时量,然后返回34。但是这样没有任何意义。而string
则不同,string
的单参数构造函数的参数是const char *
,因此凡是在需要用到string
的地方都可以用const char *
来代替(字面值就是const char *
)。如:
void print(std::string);print("hello world");
使用2.6.1节的 Sales_data
类,解释下面的初始化过程。如果存在问题,尝试修改它。
Sales_data item = {"987-0590353403", 25, 15.99};
解:
Sales_data
类不是聚合类,应该修改成如下:
struct Sales_data { std::string bookNo; unsigned units_sold; double revenue;};
定义你自己的Debug
。
解:
class Debug {public: constexpr Debug(bool b = true) : hw(b), io(b), other(b) { } constexpr Debug(bool h, bool i, bool o) : hw(r), io(i), other(0) { } constexpr bool any() { return hw || io || other; } void set_hw(bool b) { hw = b; } void set_io(bool b) { io = b; } void set_other(bool b) { other = b; } private: bool hw; // runtime error bool io; // I/O error bool other; // the others};
Debug
中以 set_
开头的成员应该被声明成constexpr
吗?如果不,为什么?
解:
不能。constexpr
函数必须包含一个返回语句
7.5.5节的Data
类是字面值常量类吗?请解释原因。
解:
不是。因为std::string
不是字面值类型。
编写你自己的Account
类。
解:
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();
下面的静态数据成员的声明和定义有错误吗?请解释原因。
//example.hclass Example {public: static double rate = 6.5; static const int vecSize = 20; static vector vec(vecSize);};//example.c#include "example.h"double Example::rate;vector Example::vec;
解:
rate
应该是一个常量表达式。而类内只能初始化整型类型的静态常量,所以不能在类内初始化vec
。修改后如下:
// example.hclass Example {public: static constexpr double rate = 6.5; static const int vecSize = 20; static vector vec;};// example.C#include "example.h"constexpr double Example::rate;vector Example::vec(Example::vecSize);
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/119304.html
小编写这篇文章,主要目的还是给大家讲一下关于python代码的相关事宜,比如怎么才能够实现自动生产C++代码,这里面还是比较的复杂的,下面小编就给大家贴出具体的代码给大家来看下。 遇到的问题 工作中遇到这么一个事,需要写比较多的C++的底层数据库类,但这些类大同小异,无非是增删改查,如果人工来写代码,既费力又容易出错;而借用python的代码自动生成,可以轻松搞定; (类比JAVA中的H...
摘要:贡献者飞龙版本最近总是有人问我,把这些资料看完一遍要用多长时间,如果你一本书一本书看的话,的确要用很长时间。为了方便大家,我就把每本书的章节拆开,再按照知识点合并,手动整理了这个知识树。 Special Sponsors showImg(https://segmentfault.com/img/remote/1460000018907426?w=1760&h=200); 贡献者:飞龙版...
摘要:协程定义协程的底层架构是在中定义,并在实现的。为了简化,我们会使用装饰器预激协程。执行上述代码结果如下出错的原因是发送给协程的值不能加到变量上。示例使用和方法控制协程。 最近找到一本python好书《流畅的python》,是到现在为止看到的对python高级特性讲述最详细的一本。看了协程一章,做个读书笔记,加深印象。 协程定义 协程的底层架构是在pep342 中定义,并在python2...
阅读 1619·2021-09-26 09:55
阅读 1358·2021-09-23 11:22
阅读 2685·2021-09-06 15:02
阅读 2602·2021-09-01 11:43
阅读 3907·2021-08-27 13:10
阅读 3655·2021-08-12 13:24
阅读 2056·2019-08-30 12:56
阅读 2976·2019-08-30 11:22