利用访问说明符加强类的封装性;
定义在public说明符之后的成员在整个程序内可以被访问,public成员定义类的接口;
定义在private说明符之后的成员可以被类的成员函数访问,private部分隐瞒了类的实现细节。
例:
class Sales_data {
public:
Sales_data() = defult;
Sales_data(const std::string &s) :bookNo(s) {}
Sales_data(const std::string &s,unsigned n,double p):
bookNo(s),units_sold(n),revenue(p*n) {}
Sales_data(std::istream &s);
std::string isbn() const {return bookNo;}
Sales_data& combine(const Sales_data&);
private:
double avg_price() const {return units_sold ? revenue/units_sold : 0;};
std::string bookNo;
unsigned units_sold =0;
double revenue =0;
};
**使用class或struct关键字**
使用struct关键字,则定义在第一个访问说明符之前的成员是public,若用class关键字,则这些成员是private。
1.友元
类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为他的友元(friend)。如果类
想把一个函数作为它的友元,只需要增加一条以friend关键字开始的函数声明语句即可。
例:
class Sales_data {
//为Sales_data的非成员函数所做的友元声明
friend Sales_data add(const Sales_data&,const Sales_data&);
friend std::istream &read(std::istream&, Sales_data&);
friend std::ostream &print(std::ostream&, const Sales_data&);
public:
Sales_data() = defult;
Sales_data(const std::string &s) :bookNo(s) {}
Sales_data(const std::string &s,unsigned n,double p):
bookNo(s),units_sold(n),revenue(p*n) {}
Sales_data(std::istream &s);
std::string isbn() const {return bookNo;}
Sales_data& combine(const Sales_data&);
private:
double avg_price() const {return units_sold ? revenue/units_sold : 0;};
std::string bookNo;
unsigned units_sold =0;
double revenue =0;
};
//Sales_data接口的非成员组成部分的声明
Sales_data add(const Sales_data&,const Sales_data&);
std::istream &read(std::istream&, Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
**友元的声明**
希望用户能够调用某个友元函数,那么我们在友元函数的声明之外再专门对函数进行一次声明,我们通常将友元的声明
与类本身放置在同一个头文件中(类的外部)。
2.类的其他特性
定义两个相互关联的类:Screen和Window_mgr
**定义一个类型成员**
class Screen{
public:
typedef std::string::size_type pos;
private:
pos cursor = 0;
pos height =0; width =0; //三个size_type类型的成员,表示光标的位置及屏幕的高和宽
std:: string contents; //保存Screen内容
};
(1)typedef 也可以等价的使用类型别名,如下所示:
class Screen{
public:
//使用类型别名等价地声明一个类型名字
using pos = std::string::size_type'
}
(2)用来定义类型的成员必须先定义后使用。
**Screen类的成员函数**
class Screen{
public:
typedef std::string::size_type pos;
//需要默认的构造函数,必须显式的把它声明出来
Screen() =default; //因为Screen()有另外一个构造函数,所以本函数是必须的
//cursor 被其类内初始值初始化为0
Screen(pos ht,pos wd,char c):height(ht),width(wd),
contents(ht *wd,c) {}
char get() const //读取光标处的字符
{return contents[cursor];} //隐式内联
inline char get(pos ht, pos wd) const; //显示内联
Scereen &move(pos r, pos c);
private:
pos cursor = 0;
pos height =0; width =0; //三个size_type类型的成员,表示光标的位置及屏幕的高和宽
std:: string contents; //保存Screen内容
};
**令成员作为内联函数**
定义在类内部的成员函数是自动inline的。(内联函数的优点:提高程序的执行效率)
在类的内部把inline作为声明的一部分显式地声明成员函数,同样的,也能在类的外部用inline关键字修饰函数的定义:
**重载成员函数**
**可变数据成员**
**类数据成员的初始值**
在C++11新标准中,可在类的定义时赋一个默认初始化的值。
3.类之间的友元关系
例如:为Window_mgr添加一个名为clear的成员,它负责把一个指定的Screen的内容设置为空白。为了
完成这一任务,clear需要访问Screen的私有成员,若想令这种访问合法,Screen需要把Window_mgr指定成它的友元。
class Screen {
//Window_mgr的成员可以访问Screen类的私有部分
friend class Window_mgr;
//Screen类的剩余部分
};
如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在类的所有成员。则,我们可以将
Window_mgr的clear成员写成如下形式:
class Window_mgr {
public:
// 窗口中每个屏幕的编号
using ScreenIndex = std::vector <Screen>::size_type;
//按照编号将指定的Screen重置为空白
void clear(ScreenIndex);
private:
std::vector <Screen> screens{Screen(24,80,'')};
};
void Window_mgr::clear(ScreenIndex i)
{
//s是一个Screen的引用,指向我们想要清空的那个屏幕
Screen &s =screen[i];
//将那个选定的Screen重置空白
s.contents = string(s.height *s.width,'');
}
**令成员函数作为友元**
class Screen {
//Window_mgr::clear 必须在Screen类之前被声明
friend void Window_mgr::clear(ScreenIndex);
};
**函数重载和友元**
如果一个类想把一组重载函数声明为它的友元,它需要对这组函数中的每一个分别声明:
// 重载的storeOn函数
extern std::ostream& storeOn(std::ostream&, Screen &);
exterm BitMap& storeOn (BitMap &, Screen &);
class Screen{
//storeOn的ostream版本能访问Screen对象的私有部分
friend std::ostream& storeOn (std::ostream& ,Screen &);
};
**友元声明和作用域**
在类的内部定义该友元函数,我们也必须在类的外部提供相应的声明从而使得函数可见。我们仅仅是用声明友
元的类的成员调用该友元函数,它也必须是被声明过的:
struct X{
friend void f() {/*友元函数可以定义在类的内部*/}
X() {f();} //错误:f还没有被声明
void g();
void h();
};
void x::g() {return f():} //错误:f还没有被声明
void f(); //声明那个定义在X中的函数
void X::h(); {return f();} //正确:现在f的声明在作用域中了