C++Primer 学习(类 二)访问控制与封装

目前为止,我们的类都还没有封装,也就是说,用户可以直达Sales_data对象的内部并且控制它的具体实现细节。

我们可以使用**访问说明符(access specifiers)**加强类的封装性:

  • 定义在public说明符之后的成员在整个程序内可被访问,因此public成员可以定义类的接口。

  • 定义在private说明符之后的成员可以被类的成员函数访问,但是不能被使用该类的代码访问,所以private部分封装了(即隐藏了)类的实现细节。

我们加入访问说明符重新定义Sales_data类,如下所示:

class Sales_data{
public:    
      //添加了访问说明符
      Sales_data () = default;
      Sales_data (const std: :string &s, unsigned n, double p):bookNo (s), units_sold (n), revenue (p*n){}
      Sales_data (const std::string &s) : bookNo (s) {}
      Sales_data (std: :istream&);
      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.0;
};

作为接口的一部分,构造函数和部分成员函数(即isbn和combine)紧跟在public说明符之后;而数据成员和作为实现部分的函数则跟在private说明符后面。

一个类可以包含0个或多个访问说明符,**而且对于某个访问说明符能出现多少次也没有严格限定。**每个访问说明符指定了接下来的成员的访问级别,其有效范围直到出现下一个访问说明符或者到达类的结尾处为止。

  • 如果我们使用struct关键字,则定义在第一个访问说明符之前的成员是public的;
  • 如果我们使用class关键字,则这些成员是private的。

使用class和struct定义类唯一的区别就是默认的访问权限。

友元

既然Sales data的数据成员是private的,我们的read、print和add函数也就无法正常编译了,这是因为尽管这几个函数是类的接口的一部分,但它们不是类的成员。

类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元(friend)。如果类想把一个函数作为它的友元,只需要增加一条以friend关键字开始的函数声明语句即可:

扫描二维码关注公众号,回复: 14669147 查看本文章
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 () = default;
      Sales_data (const std: :string &s, unsigned n, double p) 
      :bookNo (s), units_sold (n), revenue (p*n) {}
      Sales_data (const std: :string &s) : bookNo (s) {}
      Sales_data (std::istream&);std::string isbn () const{ return bookNo;} 
      Sales_data &combine (const Sales_data&);
private:
      std::string bookNo;
      unsigned units_sold = 0;
      double revenue = 0.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&);

友元声明只能出现在类定义的内部,但是在类内出现的具体位置不限。友元不是类的成员也不受它所在区域访问控制级别的约束。

一般来说,最好在类定义开始或结束前的位置集中声明友元。

封装有两个重要的优点:

  • 确保用户代码不会无意间破坏封装对象的状态。

  • 被封装的类的具体实现细节可以随时改变,而无须调整用户级别的代码。

友元的声明

友元的声明仅仅指定了访问的权限,而非一个通常意义上的函数声明。如果我们希望类的用户能够调用某个友元函数,那么我们就必须在友元声明之外再专门对函数进行一次声明。

为了使友元对类的用户可见,我们通常把友元的声明与类本身放置在同一个头文件中(类的外部)。因此,我们的Sales data头文件应该为read、print和add提供独立的声明(除了类内部的友元声明之外)。

注意:

许多编译器并未强制限定友元函数必须在使用之前在类的外部声明。

一些编译器允许在尚无友元函数的初始声明的情况下就调用它。不过即使你的编译器支持这种行为,最好还是提供一个独立的函数声明。这样即使你更换了一个有这种强制要求的编译器,也不必改变代码。

猜你喜欢

转载自blog.csdn.net/xu_fu_yong/article/details/123487596