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++类和对象访问权限、类域以及封装之间的关系。

相关推荐
clint4561 天前
C++进阶(1)——前景提要
c++
夜悊1 天前
C++代码示例:进制数简单生成工具
c++
郝学胜_神的一滴1 天前
CMake 021: IF 条件判据详诠
c++·cmake
_wyt0012 天前
洛谷 B3930 [GESP202312 五级] 烹饪问题 题解
c++·gesp
玖玥拾2 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
один but you2 天前
constexpr函数
c++
凡人叶枫2 天前
Effective C++ 条款41:了解隐式接口和编译期多态
java·开发语言·c++·effective c++
凡人叶枫2 天前
Effective C++ 条款42:了解 typename 的双重意义
java·linux·服务器·c++
小胖xiaopangss2 天前
BRpc使用
c++·rpc
-森屿安年-2 天前
63. 不同路径 II
c++·算法·动态规划