当前位置: 首页 > news >正文

五、实现隐藏(Hiding the Implementation)

五、实现隐藏(Hiding the Implementation)

5.1 设定限制(Setting limits)

  • 为什么需要限制访问?
    1. 防止用户程序员误操作内部成员。
    2. 支持类库设计者随之修改内部实现,不影响外部代码。
  • 为什么用class而不是struct?
    1. class 默认成员是private ,而struct 默认是public
    2. class 更有利于隐藏实现细节 和限制接口

5.2 访问控制(Access Control)

实现隐藏

  • 访问控制常常被称为实现隐藏(implementation hiding)。
  • 访问控制在抽象数据类型的内部设置了边界,出于两个原因:
    • 为了将接口(interface)与实现(implementation)分离。
    • 为了明确客户端程序员能用和不能用的地方

关键字说明

  • public :所有人都可以访问,
  • private :只有本类成员函数和类自己能访问。
  • protected :类似private ,唯一的区别是:继承类(“Inherited” class) 可以访问protected 成员(但不能访问 private 成员),也就是说类自己和子类可以访问。
//C05:Private.cpp
//Setting the boundaryclass B
{
private:char j;float f;
public:int i;void func();
};void B::func()
{i = 0;j = '0';f = 0.0;
};void main()
{B b;b.i = 1;//OK.public//! b.j = '1'; // Illegal,private//! b.f = 1.0; //Illegal,private
}

类的格式

class X
{
public:void interface_fun();
private:void private_fun();int internal;int mx;
};
class X
{void private_fun();int internal;int mX;
public:void interface_fun();
};

class 默认是private

5.3 友元(Friends)

什么是friend

  • 友元函数是一种有特权的函数。

  • 允许外部函数(友元函数)或类(友元类)访问本类的私有成员。

  • 一个普通的public成员函数具备三个逻辑上不同的特性:

    • 该函数可以访问类对象的私有部分。
    • 该函数属于类的作用域中。(这个函数是在类的”命名空间里面“定义的,它是类的一部分)
    • 该函数必须通过对象调用(拥有一个this 指针)。
  • 一个public static 成员函数仅具有前两个特性。

  • 一个friend 函数仅具有第一个特性。

friend的作用

  • 友元(friend)可以提高效率
  • friend不能被继承(无论私有继承还是公有继承)
  • 我们可以将一个全局函数(global function) 声明为友元;也可以将另一个类的成员函数 ,甚至整个类声明为友元。

Example1:一个全局函数作为一个类友元

//Example1
#include <iostream>
using namespace std;
class Time
{int hours,minutes;
public:void set(int nhours,int nminutes){hours = nhours;minutes = nminutes;}friend void show(Time& time);//friend functions
};void show(Time& time)//no prefix "friend"
{//access private numberscout << time.hours << ":" << time.minutes << endl;
}void main()
{Time time;time.set(20,30);show(time); // not a member of "time"
}

输出:

20:30

Example2:一个全局函数作为多个类的友元

//Example
#include <iostream>
using namespace std;
calss Boat;//to be a defined later
class Car
{
public:void set(int i){weight = i;}friend int totalWeight(Car& c,Boat& b);
private:int weight;
};class Boat
{
public:void set(int i){weight = i;}friend int totalWeight(Car& c,Boat& b);
private:int weight;
};int totalWeight(Car& c,Boat& b){return c.weight+b.weight;
}void main()
{Car c;c.set(10);Boat b;b.set(8);cout << "The total weight is " << totalWeight(c,b) << endl;
}

这里之所以要先声明Boat ,因为C++的原则是使用前必须声明,如果不提前声明,那么在Car 里面的totalWeightBoat& b 作为形参就会报错

输出:

The total weight is 18

Example3:一个成员函数作为一个友元

//Example3
#include <iostream>
#include <string>
using namespace std;class Girl;//to be defined laterclass Boy
{
public:void init(string n){name = n;}void Disp(Girl& x);//member function
private:string name;
};class Girl
{
public:void init(string n){name = n;}friend void Boy::Disp(Girl& x);//friend
private:string name;
};void Boy::Disp(Girl& x)
{cout << "Boy's name is " << name << endl << "Girl's name is " << x.name << endl;
}void main()
{Boy b;b.init("Bob");Girl g;g.init("Kitty");b.Disp(g);
}

输出:

Boy's name is Bob
Girl's name is Kitty

这个样例正式因为Boy::Disp(Girl& x)Girl 声明为friend ,所以它才可以执行x.name

Example4:一个类作为一个友元

//Example4
#include <iostream>
using namespace std;
class X
{
public:void Set(int i){x = i;}void Display(){cout << "x=" << x << "," << "y=" << y << endl;}
private:int x;static int y;friend class Y;//friend class 没有事先声明也可以
};int X::y = 1;//类外初始化静态成员变量class Y
{
public:void Set(int i,int j);void Display();
private:X a;//member object
};void Y::Set(int i,int j)
{a.x = i; //using private members of aX::y = j;//a::y? a.y?
}void Y::Display()//using private members of a
{cout << "x=" << a.x << ",";cout << "y=" << X::y << endl;
}void main()
{X b;b.Set(5);b.Display();Y c;c.Set(6,9);c.Display();b.Display();
}

输出:

x=5,y=1
x=6,y=9
x=5,y=9

C++ 中,friend class 语句 不需要提前声明该类,编译器会自动隐式地把 Y 当作类名并接受,即便此时它还没定义。C++ 编译器在处理 friend 时是宽容的,它允许你声明一个类是朋友,即使它还没有被声明或定义,因为编译器知道之后可能会出现 Y 的定义,它会延迟检查这个类是否存在。

这里对statci进行一下介绍

static

在C++中,static 成员变量或成员函数表示“属于类本身而不是对象 ”。这意味着:

  1. 所有对象共享一份static 变量

    • statci 成员变量在类中声明,在类外定义
    • 它的声明周期贯穿程序整个运行周期
    • 不管有多少个对象,static 成员变量只有一份内存拷贝。
  2. static 成员变量的定义在类外进行有且仅有一次初始化:

    int x::y = 1;
    

    这行代码就是对静态成员变量y 进行初始化,只能写一次,而且必须在类外

  3. 访问方式:

    • 类名访问:X::y

    • 或者对象访问:obj.y (虽然能访问,但推荐用类名访问以强调这是类共享的)

        X::y = j;//a::y? a.y?
      

      所以这里a.y 是可以的,但a::y 不可以,因为:: 要求左边是类型名命名空间 ,而不是对象。

5.4 类(The class)

类的结构基本样式:

class Date 
{
public:void SetDate(int y,int m,int d);int IsLeapYear();void Print();
private:int year,month,day;
};
//member function's definition

成员函数定义:

  • 使用作用域解析符::
class Date 
{
public:void SetDate(int y,int m,int d);int IsLeapYear();void Print();
private:int year,month,day;
};void Data::SetDate(int y,int m,int d)
{year = y;month = m;day = d;
}

实例

  • Handle类隐藏实现细节(Pimpl手法)

Handle.h

//C05:Handle.h
//Handle calsses
#ifndef HANDLE_H
#define HANDLE_H
class Handle
{struct Cheshire;//declaration onlyCheshire* smile;
public:void initialize();void cleanup();int read();void change(int);
};
#endif

Handle.cpp

//C05:Handle.cpp
//Handle implementation
#include "Handle.h"
#incldue <cassert>struct Handle::Cheshire
{int i;
};
…………

UseHandle.cpp

//C05:Use.Handle.cpp
#include "Handle.h"int main()
{Handle u;u.initialize();u.read();u.change(1);u.cleanup();
}

5.5 总结(Summary)

  • 访问控制: 控制接口和实现分离
  • friend: 特殊访问权限
  • 静态数据成员: 类级别共享数据
  • Handle类设计: 实现与接口分离的典范
http://www.xdnf.cn/news/106399.html

相关文章:

  • 记录一次OGG进程abended,报错OGG-01431、OGG-01003、OGG-01151、OGG-01296问题的处理
  • Windows 同步技术-一次性初始化
  • Discuz!与DeepSeek的AI融合:打造智能网址导航新体验——以“虎跃办公”为例
  • 15.FineReport动态展示需要的列
  • 运维案例:让服务器稳定运行,守护业务不掉线!
  • 块压缩与图片压缩优缺点对比
  • 高可靠性厚铜PCB生产的五大关键设备
  • leetcode刷题——判断对称二叉树(C语言版)
  • 知识链(Chain-of-Knowledge):通过对异构来源的动态知识适配实现大语言模型的知识落地
  • 数据通信学习笔记之OSPF的基础术语-距离矢量路由协议
  • 概率dp总结
  • 精准识别违规登录:Windows事件ID 4624全维度分析手册
  • 解决AWS中ELB的目标群组中出现不正常数
  • JAVA工程师面试题(一)
  • 在串的简单模式匹配中,当模式串位j与目标串位i比较时,两字符不相等,则i的位移方式是?
  • 快速生成安卓证书并打包生成安卓apk(保姆教程)
  • HCIP-OSPF综合实验
  • Linux网络编程 从集线器到交换机的网络通信全流程——基于Packet Tracer的深度实验
  • 第十篇:系统分析师第三遍——7、8章
  • Kubernetes服务自动注册Consul全攻略 - 基于consul-register的实践指南
  • vue3:十一、主页面布局(修改顶部导航栏样式-左侧,页面名称设置)
  • Vue3:大纲思路
  • 深入解析C++ STL Stack:后进先出的数据结构
  • Linux CAN 驱动浅析
  • YOLO11改进-Backbone-引入TransXNet替换YOLO backbone 学习全局和局部动态信息,提高检测精度
  • 面试经历(一)雪花算法
  • gem5 笔记01 gem5 基本应用流程
  • 【敏矽微ME32G030系列】介绍、环境搭建、工程测试
  • 2022 年 9 月青少年软编等考 C 语言六级真题解析
  • 基于PaddleOCR对图片中的excel进行识别并转换成word(一)