今天是第七篇笔记,主要是类相关的基础知识。其实越往后看越发觉得翻译的质量略有待考证。不知是我水平不够理解有误,还是译者笔下误。总之有的地方讲的着实混乱,看的着实头大。昨天看了些大牛的文章,自卑感油然而生。有时候,不得不承认,信息负载的确不是好事。当你坚持做一件事情时,不妨做一个井底之蛙。等你做完了事情,出来观天下吧。
若有错误 请指正 谢谢
友元只能声明在类的内部,位置不限。友元不是类的一部分,不受访问权限的控制。
若希望类的用户可以调用友元函数,那么除了在类的内部声明一次之外,还需要在类外在进行一次专门的声明,不过有的编译器不限制。原因是友元只是指定了朋友这个关系,并不是一般意义上的函数声明,故需要二次声明。
类自己定义了一个作用域。所以存在同名隐藏和名字查找相关内容。
名字查找:寻找与所使用的名字最佳匹配过程。
流程:先在块内寻找声明,只寻找使用之前的声明。若块内未发现,则在外层作用域中寻找。若都未发现,则报错。
类部分符合此规则,但是遵循先处理声明后处理函数的原则。
当发生同名隐藏时,仍然可以通过显式使用域作用付来使用被隐藏的变量。当形参和数据成员同名时,会覆盖隐藏,这个时候可以通过使用this指针或者域作用符来使用数据成员。
7.1
//头文件
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include
#include
#include
#include
class Sales_data{
private:
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
public:
Sales_data(){}
std::string isbn() const{
return this->bookNo;
}
Sales_data& combine(const Sales_data&);
double avg_price() const;
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
};
#endif // !SALES_DATA_H
//函数定义部分
#include "Sales_data.h"
using namespace std;
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){
this->units_sold += rhs.units_sold;
this->revenue += rhs.revenue;
this->bookNo = rhs.bookNo;
return *this;
}
Sales_data add(const Sales_data&rh1, const Sales_data&rh2){
Sales_data temp;
temp.units_sold = rh1.units_sold + rh2.units_sold;
temp.revenue = rh1.revenue + rh2.revenue;
temp.bookNo =
rh1.bookNo;
return temp;
}
ostream& print(ostream& os, const Sales_data& rhs){
os << rhs.isbn() << " " << rhs.units_sold << " "
<< rhs.revenue << " " << rhs.avg_price();
return os;
}
istream& read(istream& is, Sales_data& rhs){
double price = 0.0;
is >> rhs.bookNo >> rhs.bookNo >> price;
rhs.revenue = price*rhs.units_sold;
return is;
}
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) << endl;
}
else
cerr << "No data ?" << ndl;
}
7.2 7.3
参考7.1
7.4
#ifndef Person_H
#define Person_H
#include
#include
class Person{
private:
string name, Address;
};
#endif // !Person_H
7.5
class Person{
private:
string name, address;
public:
string Get_name()const{
return name;
}
string Get_addr() const{
return address;
}
};
应是const的,因为我只需要获得信息,无需修改信息。
7.6 7.7
参考7.1
7.8
因为读的过程涉及写值,一定定义成const,那么就无法顺序写入信息。
在print函数中,我们只是输出信息,所以可以定义成const.
7.9
friend ostream& print(ostream& os, const Person& rhs){
os << rhs.name << " " << rhs.address;
return os;
}
friend istream& read(istream& is, const Person& rhs){
is >> rhs.name >> rhs.address;
return is;
}
7.10
读取data1,data2.然后检测istream对象。
7.11
int main(){
Sales_data item1;//默认构造函数调用
Sales_data item2("Hello");//const string& 的构造函数调用。
Sales_data item3("Word", 10, 3.2);//三个参数的构造函数调用。
read(cin, item1);
Sales_data(cin);
system("pause");
return 0;
}
Sales_data(istream& is){
read(is, *this);
}
7.13
参考7.1
7.14
class Temp{
private:
int number = 0;
public:
Temp(int n=0) :number(n){}
};
7.15
Person(string n_name="", string a_addr="") :name(n_name), address(a_addr){}
7.16
无次数为位置限定。
接口部分应该定义在public 下。
实现部分应该定义在private下。
7.17
无大区别。唯一的区别在于默认权限。
7.18
隐藏类的实现细节部分。
7.19
把数据成员声明为private 的。
把接口函数声明为public的。
原因:略。参见实现和接口的含义。
7.20
普通函数或类想要访问类的私有成员。
坏处就是破坏封装性了。
7.21
参考7.1
7.22
参考前面习题。
7.23 7.24
#include
#include
using std::string;
class Screen{
public:
using pos =string::size_type;
private:
pos cursor=0;
pos height=0, width=0;
std::string content;
public:
Screen(){}
Screen(pos w_width, pos h_height) :height(h_height), width(w_width){
content=string(w_width*h_height, ' ');
}
Screen(pos w_width, pos h_height, char c) :height(h_height)
, width(w_width), content(h_height*w_width, c){}
};
7.25
可以安全依赖。因为都存在类内初始值,而且string 是存在默认构造函数的。
7.26
提高效率吧。。。我喜欢。。
7.27
略
7.28
会发生临时对象调用成员函数的事情。。
7.29
不正确。
7.30
缺点:多打了几个字符。
优点:同名隐藏时可以显式调用,其余也就没什么了吧。
7.31
class Y; //前向声明。
class X{
private:
Y* ptr;
};
class Y{
X itex;
};
7.32
略。
7.33
暂时未发现会导致什么情况。只不过是const成员函数而已,无法在函数体内修改成员。
7.34
标识符未定义。
7.35
根据名字查找原则,同名覆盖。使用的都是类内部定义的别名和函数。
7.36
初始化列表的执行顺序是按照数据成员的声明顺序执行的。
修改:
struct X{
x(int i,int j):base(i){
rem(base%j);
}
}
7.37
第一个是istream对象相关的构造函数。
第二个是默认构造函数调用
第三个是调用string 相关的构造函数。
7.38
没看懂题目的意思。
istream对象允许拷贝呢?
7.39
同上。如何为istream对象提供默认实参?可以拷贝?string对象倒是没问题。
7.40
日期的构成:年日月。
可以考虑用无符号整形表示吧。此时的构造函数很简单吧。
也可以用string 表示。此时构造函数提供一个string就可以了。
7.41
Sales_data(std::string temp_book, unsigned temp_solds, double temp_rev) :
bookNo(temp_book), units_sold(temp_solds), revenue(temp_rev){
cout << "Call the three arguments Cstor" << endl;
}
Sales_data() :Sales_data("", 0, 0){
cout << " Call the default Cstor " << endl;
}
Sales_data(istream& is):Sales_data(){
cout << "Call the istream Csotr" << endl;
read(is, *this);
}
Sales_data(const string& s) :Sales_data(s,0,0) {
cout << "Call the string Cstor" << endl;
}
委托构造函数至少有一个是要用来被委托的,而且我感觉用处不是很大啊。类似于函数里面调用其他构造函数进行初始化工作。
7.42
略。
7.43
C(int x=0){
NoDefault(x)
//...
}
7.44
不一定合法。如果NoDefault中没用默认构造函数,那么10个对象怎么办?所以要视情况而定。
7.45
合法。因为我有默认构造函数。10个对象我有办法处理。
7.46
a)不正确,因为我们可以让编译器提供。
b) 不是,是不用提供实参就可以调用的构造函数。
c)理论是不应该,但是要控制对象的初始化过程。
d)错误。详情可以C++对象模型的博文,其中一张深入探讨了构造函数的相关问题。
7.47
定义成显式的比较好。
如果当我们用C风格字符串进行拷贝初始化对象时,要进行两步转换,两次转换是不合法行为。
不如直接禁止隐式类型转换发生。
7.48
第一个进行隐式类型转换,会产生一个临时对象。
第二个调用string相关的构造函数。
第三个会产生隐式类型转换,把C字符传转换成string。
7.49
a)s先隐式转换成对象类型。然后拷贝给形参
b)同样是先隐式转换,然后直接引用那个临时对象,然后报错,因为普通引用类型无法引用const对象。
c)正好同b互补。因为是const的,所以调用成功。
7.50
这个翻译的我也是醉了。
原题:确定在你的Person类中是否有一些构造函数应是explicit的。
为嘛我总觉得读起来怪怪的。我语文已经差到这个地步了嘛?
关于构造函数是否是Explicit的。关键在于自己是否想禁止隐式类型转换。
7.51
我理解是可能是二义性问题。一个单参数如何理解?是表示数量还是一个元素的值?
但是string就不会产生这个问题吧。比较C风格字符串同string还是有关联的。
7.52
列表初始化。等价于写成:
Sales_data item("978-xxxxxxxx",25,1.99);
前提是你要有相应的构造函数。
7.53
由于这部分比较偏僻,带的需要的时候在来仔细研究吧。
7.54
同7.53
7.55
不是字面值常量类。因为字面值常量类至少要存在一个constexpr类型的构造函数。
7.56
最直观的感受就是属于所有对象,但是不属于任何一个对象。共有的东西。
其次就是生命周期是贯穿程序的。
优点可能具体情况讨论比较好。毕竟优点缺点是比较出来的。
7.57
#include
using namespace std;
class Account{
public:
static double rate() { return interestRate;}
static void rate(double);
private:
static constexpr int period=30;
double daily_tbl[period];
}
7.58
有错误。
首先在类内部初始化了普通静态数据成员。
其次vecsize 不可以用了vec的大小。
反正我感觉这个题目出的简直操蛋啊,从来不写一个程序。而且有的题目意思理解起来坑爹啊。所以有的题目,有的知识比较偏僻的我直接就省略了。希望下面的习题千万不能出成这个样子,其实我更希望割裂习题间的联系,因为来回翻看前面的也是比较烦心的事情啦。
其实对比前面的博文,我发现这个篇章我写的的确比较少。其实我也是在告诫我自己,你要做的是学会知识,而不是把书抄下来。