7.2 访问与控制
访问说明符
public成员定义类的接口,public说明符之后的成员可以在整个程序中被访问
private可以被类的成员函数访问,用户无法直接进行访问
作用范围从一个访问说明符开始,到另一个访问说明符或者类尾为止
class和struct关键字
两个关键字都可以用来定义类,但是默认访问权限不同。
struct定义类 在说明符之前的成员是public的
class定义类 在说明符之前的成员是private的
可变成员函数
想要类内的某个数据成员可以被修改,即使是在const,只需要在定义的时候加上mutable关键字
友元
如果将数据设计成private的,那么很多非接口函数将无法正常编译,比如read/print,这些函数虽然是接口的一部分,但是 他们不是类的成员函数。友元就是允许卡类或者函数访问它非公有成员的一部分。
友元与非成员函数
如果类想把一个函数作为它的友元,需要在声明的时候加上一个关键字 friend,友元声明只能出现在类内部,具体出现的位置不限,友元声明仅仅只是指定权限,如果希望用户调用这个函数还需要在头文件里再次声明。
类之间的友元
友元除了可以定义非成员函数以外还可以定义其他类或者其他类的成员函数为友元。如果一个类想要访问另外一个类的非公有成员在内的所有成员,就需要将其他类声明成友元。
class a{}
class b{
friend class a ;}
类b将类a声明成友元,这样类a就可以使用类b中的私有成员,但是需要注意友元不具有传递性,b可以访问a的私有,如果b有自己的友元类,那这个友元类只能访问b,不能访问a。换句话说就是每个类控制和管理自己的友元类和友元函数。
友元与成员函数
可以将某个成员函数提供权限,这个函数需要指明是哪个类的
class a{
void move (&set)
}
class b{
friend a::move(&set) ;}
a::move需要在b类之间被声明过。比如在上面的例子中类b仅仅只给类a中的成员函数move()提供访问权限,完成这个操作需要仔细组织程序的结构,比如如果想让上面的函数通过编译,需要先定义类a,在类a中声明move函数但是不能定义它,然后定义类b,并在类b中声明友元,然后再定义move(),此时move()函数才可以使用b的成员。
返回*this的成员函数
inline Screen &Screen::set(char c)
{return *this}
是将对象作为一个左值返回,使用 Screen&的含义是返回对象而非对象的副本,如果不是引用类型就只能改变临时副本的值而不是对象。
一个const成员函数如果以引用形式返回*this,返回类型是常量引用。
类类型
就算两个类的列表相同,他们也是不同的类型。
类声明
class Sales_data;
类只声明不定义就是一个不完全类,不能被使用,这种声明方式是前向声明,类在定义的时候可以有指向它自己的指针或引用。
类 类型转换
类类型转换
聚合类
聚合类的特点:
- 所有成员都是public的
- 没有定义任何构造函数
- 没有类内初始值
- 没有基类,也没有virtual函数
字面值常量
字面值类型的类可能含有constexpr函数成员 ,他们是隐形的const。构造函数不能是const,但是字面值的类可以是constexpr,并且可以被声明成=default,否则既要符合构造函数的要求,也要符合constexpr的要求
类的静态成员
在声明类成员时加上static关键字,静态成员存在于任何对象之外,与对象无关,不与对象绑定也不包含this指针,不能被声明成const,定义静态成员时可以在类内也可以在类外,可以不加static关键字
使用静态成员
使用静态成员的方式有两种,一种是利用作用运算符,另一种是类的对象、引用、指针访问静态成员。
静态成员详解
书店程序
写入两本书的isbn,销售单价和销售数量,如果两本书不属于同一本书报错,如果是同一本书返回总出售册数,总销售额和平均售价。
Sales_data.h
#ifndef SALES_DATA_H_INCLUDE
#define SALES_DATA_H_INCLUDE
#endif
#include<string>
#include<iostream>
using namespace std;
class Sales_data
{
private:
string bookisbn;
int units_sold=2;//出售册数和出售价格
double revenue=20,saleprice=10;
public:
string isbn()const
{
return bookisbn;
}
Sales_data& combine(const Sales_data& rhs );//对象出售总数和总收入的和
double avg_price() const; //计算平均售价
friend istream &read(istream &is,Sales_data &item); //声明成友元
friend ostream &print(ostream &os,const Sales_data &item); //声明成友元
};
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.bookisbn>>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;
}
source.cpp
#include<string>
#include<iostream>
#include"Sales_data.h"
using namespace std;
int main()
{
Sales_data book1,book2,book;
read(cin,book1);
read(cin,book2);
if(book1.isbn()==book2.isbn())
print(cout,add(book1,book2));
else
cout<<"读入错误,请重新读入"<<endl;
return 0;
}