构造函数与友元
上一篇的类其实都不能算是个类,连构造函数都没有,连访问控制都没有,这类没法用啊(虽然编译器会自动生成默认的构造函数)。今天,我们就来加上它们。顺便再来个友元。
还是,先直接贴代码。
Sales_data.h
#pragma once
#include<iostream>
using namespace std;
class Sales_data
{
friend istream& read(istream&, Sales_data&);//友元允许其他类或者函数访问其非公有成员
friend ostream& print(ostream&, const Sales_data&);
friend Sales_data add(Sales_data&, 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* n) {};
Sales_data(istream&);
string isbn() const;
Sales_data& combine(const Sales_data&);
private:
double avg_price() const;
string bookNo;
unsigned units_sold = 0; //本书销量
double revenue = 0.0;//总收入
};
//Sales_data的非成员接口函数
//它们是一些辅助函数
istream& read(istream&, Sales_data&);
ostream& print(ostream&, const Sales_data&);
Sales_data add(Sales_data&, Sales_data&);
Sales_data.cpp
#include<iostream>
#include"Sales_data.h"
using namespace std;
Sales_data::Sales_data(istream& in)
{
read(in, *this);
}
string Sales_data::isbn() const
{
return bookNo;
}
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
{
return revenue / units_sold;
}
istream& read(istream& in, Sales_data& item)
{
double price = 0;
in >> item.bookNo >> item.units_sold >> price;
item.revenue = item.units_sold * price;
return in;
}
ostream& print(ostream& out, const Sales_data& item)
{
out << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();
return out;
}
Sales_data add(Sales_data& lhr, Sales_data& rhs)
{
Sales_data sum = lhr; //此时调用拷贝构造函数
sum.combine(rhs);
return sum;
}
来,下面来先说一下构造函数。
1. 构造函数的名字与类名相同,没有返回值
2. 在第一篇中,我们没有定义构造函数,那为什么我们还能声明对象呢?原因就是:编译器给我们合成了默认的构造函数。但请特别注意: 只有我们的类没有显式的定义任何构造函数时,编译器才会为我们生成默认构造函数。一旦我们定义了一些其他的构造函数,那么除非我们再定义一个默认构造函数,否则类将没有默认构造函数
合成的默认构造函数按如下规则初始化:
- 如果数据成员存在类内初始值,则给它赋类内初始值。
- 如果不存在,则默认初始化。
3. 但我们的大多数类都不能依赖于合成的默认构造函数,原因(我只能看懂第一条。。):也就是上面说的,我们的编译器只有在类中没有任何构造函数的时候,才会给我们提供默认构造函数,一旦类中有构造函数,那我们的类就缺少了默认构造函数。
因此,我们在一个类中,可以通过 =default 的方式来 显式的要求编译器生成默认构造函数。我们 定义这个构造函数的目的 仅仅是因为我们既需要其他构造函数,也需要默认的构造函数。我们希望这个函数的作用完全等同于之前使用的合成的默认构造函数。
4. 再来看下面的两个构造函数,它们都有成员初始化列表,但在成员初始化列表中忽略的值,它们将以与合成默认构造函数相同的方式隐式初始化。
5. 再看下面那个比较不一样的构造函数,该构造函数调用了read函数来对其成员进行初始化。
6. 再下面再来看一下访问控制。
public:代表这个数据成员或成员函数既能在类内访问,又能被类外对象访问。
private:代表这个数据成员或成员函数只能在类内访问,不能通过类外对象访问。
7. 定义完访问控制,我们会 “神奇” 的发现,我们的read,print以及add函数报错了。为什么?因为我们把类内的数据成员定义为了私有,而我们的read,print,add函数又不是类内的成员函数,因此不能通过类外的对象访问类的数据成员。那怎么办呢?有一个好办法,就是声明 友元
当我们另其他类或者函数成为某个类的友元之后,那么这些其他类或者函数就可以访问这个类的非公有成员。而且声明友元的方式也非常的简单,就像上面那样 直接+一个friend 就好了。这样我们就会发现,错误没有了!