C++学习笔记30:友元类、内部类和封装

目录

一、友元类是什么?

二、友元类的简单例子

三、友元类的特点

[1. 友元关系是单向的](#1. 友元关系是单向的)

[2. 友元关系不能传递](#2. 友元关系不能传递)

四、什么是内部类?

五、内部类的特点

六、封装是什么?

七、为什么要封装?

八、友元和封装的关系

九、小结


一、友元类是什么?

前面学习了友元函数:

cpp 复制代码
friend void PrintDate(const Date& d);

友元函数可以让一个普通函数访问类的私有成员。

而友元类是让一个类可以访问另一个类的私有成员。

写法是:

cpp 复制代码
friend class 类名;

例如:

cpp 复制代码
class Date {
    friend class Time;

private:
    int _year;
    int _month;
    int _day;
};

这表示:

Time 类是 Date 类的友元类,Time 类中的成员函数可以访问 Date 类中的私有成员。


二、友元类的简单例子

cpp 复制代码
#include <iostream>
using namespace std;

class Date {
    friend class Time;
public:
    Date(int year = 2024, int month = 1, int day = 1)
        : _year(year)
        , _month(month)
        , _day(day)
    {}

private:
    int _year;
    int _month;
    int _day;
};

class Time {
public:
    void PrintDate(const Date& d) {
        cout << d._year << "-" << d._month << "-" << d._day << endl;
    }
};

int main() {
    Date d(2024, 5, 1);

    Time t;
    t.PrintDate(d);

    return 0;
}

运行结果:

cpp 复制代码
2024-5-1

这个例子中,_year、_month、_day 都是 Date 的私有成员。

正常情况下,time 类是不能直接访问它们的。

但是因为 Date 中声明了:

cpp 复制代码
friend class Time;

所以 Time 类就可以访问 Date 的私有成员。


三、友元类的特点

友元类有以下几个特点:

cpp 复制代码
1. 友元类可以访问另一个类的滴油成员;
2. 友元关系是单向的;
3. 友元关系不能传递;
4. 友元会破坏封装,不能滥用。

1. 友元关系是单向的

如果写:

cpp 复制代码
class Date {
    friend class Time;
};

表示 Time 类可以访问 Date 的私有成员。

但是不代表 Date 类可以访问 Time 的私有成员。

如果要 Date 访问 Time 类的私有成员,需要在 Time 类中声明:

cpp 复制代码
class Time {
    friend class Date;
};

2. 友元关系不能传递

如果:

cpp 复制代码
A 是 B 的友元
B 是 C 的友元

不能推出:

cpp 复制代码
A 是 C 的友元

友元关系只能对当前声明的类有效。


四、什么是内部类?

内部类就是在一个类的内部再定义一个类。

例如:

cpp 复制代码
class A {
public:
    class B {
    public:
        void Print() {
            cout << "B class" << endl;
        }
    };
};

这里 B 就是 A 的内部类。

使用内部类时,需要通过类域访问:

cpp 复制代码
A::B b;
b.Print();

完整代码:

cpp 复制代码
#include <iostream>
using namespace std;

class A {
public:
    class B {
    public:
        void Print() {
            cout << "B class" << endl;
        }
    };
};

int main() {
    A::B b;
    b.Print();

    return 0;
}

运行结果:

cpp 复制代码
B class

五、内部类的特点

内部类有以下几个特点:

cpp 复制代码
1. 内部类定义在另一个类的内部
2. 使用是要通过 外部类::内部类 的方式
3. 内部类受外部类的类域限制
4. 内部类天然具有外部类的友元权限

也就是说内部类可以访问外部类的私有成员。

例如:

cpp 复制代码
#include <iostream>
using namespace std;

class A {
public:
    class B {
    public:
        void Print(const A& a) {
            cout << a._a << endl;
        }
    };

private:
    int _a = 10;
};

int main() {
    A a;
    A::B b;

    b.Print(a);

    return 0;
}

运行结果:

cpp 复制代码
10

这里 _a 是 A 的私有成员,但是内部类 B 可以访问它。

不过内部类在 C++ 中用的没有 Java 那么多,基本了解即可。


六、封装是什么?

封装是面向对象的三大特性之一。

简单说就,封装就是:

把数据和操作数据的方法放到一起,并通过访问限定符控制外部能不能直接访问。

例如:

cpp 复制代码
class Date {
public:
    void Print() {
        cout << _year << "-" << _month << "-" << _day << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

这里 _year、_month、_day 是私有成员,外部不能直接访问。

外部想使用日期,只能通过公有接口,比如:

cpp 复制代码
Print();

这就是封装。


七、为什么要封装?

封装的意义主要有两个:

cpp 复制代码
1. 保护数据
2. 降低代码之间的耦合

如果成员变量全部写成 public,外部可以随意修改:

cpp 复制代码
d._month = 100;
d._day = -1;

这样对象很容易进入错误状态。

如果把数据放到 private,再通过成员函数控制访问,就可以在函数中加入检查逻辑。

例如:

cpp 复制代码
void SetDate(int year, int month, int day) {
    if (month >= 1 && month <= 12) {
        _month = month;
    }
}

这样可以减少错误。


八、友元和封装的关系

友元可以让类外的函数或其它类访问私有成员。

这确实很方便,但是会破坏封装性。

所以友元要谨慎使用。

一般原则是:

cpp 复制代码
能用成员函数解决,就优先用成员函数
确实不适合写成成员函数时,再考虑友元

比如输入输出运算符重载:

cpp 复制代码
cout << d;
cin >> d;

这种场景下,为了保证正常输入习惯,operator<< 和 operator>> 通常携程全局函数,然后声明为友元。

这属于比较合理的友元使用场景。


九、小结

本篇主要学习了友元类、内部类和封装。

需要记住:

  1. 友元类使用 friend class 类名; 声明;
  2. 友元类可以访问另一个类的私有成员;
  3. 友元关系是单向的;
  4. 友元关系不能传递的;
  5. 内部类在是一个类的内部定义的类;
  6. 内部类使用时需要写成 外部类::内部类 ;
  7. 内部类天然具有外部类的友元权限;
  8. 封装是把数据和操作数据的方法放在一起;
  9. 封装可以保护数据,降低耦合;
  10. 友元会破坏封装,所以不能滥用。

友元和内部类本质上都是C++中突破访问限制的机制。学到他们不是为了经常使用,而是为了理解C++类和对象访问权限、类域以及封装之间的关系。

相关推荐
黄小白的进阶之路6 小时前
C++提高编程---3.6 STL-常用容器-queue 容器【P213~P214】
c++
ID_180079054736 小时前
小红书评论 API 接口详解与实战开发
java·jvm·c++
khalil10207 小时前
代码随想录算法训练营Day-58 图论08 | 拓扑排序精讲、dijkstra(朴素版)精讲
c++·算法·图论·dijkstra·拓扑排序·prim·最短距离
丘山望岳8 小时前
藤萝垂序——二叉搜索树
开发语言·数据结构·c++
光电笑映8 小时前
深入理解 ELF:从目标文件到程序加载的全过程
linux·运维·服务器·c++
lynnlovemin9 小时前
【信息学竞赛专题】滑动窗口(尺取法)超全详解|C++模板+经典例题+避坑指南
开发语言·c++·算法·滑动窗口·信息学竞赛
不会C语言的男孩9 小时前
VS Code 中搭建 C/C++ 开发环境(MSYS2 编译器)
c语言·c++
似水এ᭄往昔9 小时前
【Qt】--Qt概述
开发语言·c++·qt
澈2079 小时前
动态规划入门:从斐波那契到爬楼梯
c++·算法