• C++的类和对象(六):友元、内部类


    目录

    友元

    友元函数

    友元类

    内部类

    匿名对象

    拷贝对象时的一些编译器优化

    再次理解类和对象


    友元

    基本概念:友元提供了一种突破封装的方式,有时提供了便利,但是友元会增加耦合度,破坏了封装,所以友元不宜多用(开后门)

    格式:friend 函数声明

    分类:友元函数和友元类

    友元函数

    解决问题:类外无法访问成员函数

    注意事项:

    1、友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字

    1. class Date
    2. {
    3. friend ostream& operator<<(ostream& _cout, const Date& d);
    4. friend istream& operator>>(istream& _cin, Date& d);
    5. public:
    6. Date(int year = 1900, int month = 1, int day = 1)
    7. : _year(year)
    8. , _month(month)
    9. , _day(day)
    10. {}
    11. private:
    12. int _year;
    13. int _month;
    14. int _day;
    15. };
    16. ostream& operator<<(ostream& _cout, const Date& d)
    17. {
    18. _cout << d._year << "-" << d._month << "-" << d._day;
    19. return _cout;
    20. }
    21. istream& operator>>(istream& _cin, Date& d)
    22. {
    23. _cin >> d._year;
    24. _cin >> d._month;
    25. _cin >> d._day;
    26. return _cin;
    27. }
    28. int main()
    29. {
    30. Date d;
    31. cin >> d;
    32. cout << d << endl;
    33. return 0;
    34. }

    2、友元函数可以访问类的私有和保护成员,但不是类的成员函数

    3、友元函数不能用const修饰(没必要)

    4、友元函数可以在类定义的任何地方声明,不受类的访问限定符限制

    5、一个函数可以是多个类的友元函数

    6、友元函数的调用与普通函数的调用原理相同

    7、不想用友元就用get和set

    友元类

    注意事项:

    1、友元的关系是单向的,不具有交换性(你是我的朋友我允许你看我,但不知道你让不我看你)

    Time类中声明Date类是其友元类,则可以在Date类中访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行:

    1. class Time
    2. {
    3.   friend class Date;//声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
    4. public:
    5. Time(int hour = 0, int minute = 0, int second = 0)
    6. : _hour(hour)
    7. , _minute(minute)
    8. , _second(second)
    9. {}
    10.  
    11. private:
    12.   int _hour;
    13.   int _minute;
    14.   int _second;
    15. };
    16. class Date
    17. {
    18. public:
    19.   Date(int year = 1900, int month = 1, int day = 1)
    20.      : _year(year)
    21.     , _month(month)
    22.     , _day(day)
    23.   {}
    24.  
    25.   void SetTimeOfDate(int hour, int minute, int second)
    26.   {
    27.       // 直接访问时间类私有的成员变量
    28.       _t._hour = hour;
    29.       _t._minute = minute;
    30.       _t._second = second;
    31.   }
    32.  
    33. private:
    34.   int _year;
    35.   int _month;
    36.   int _day;
    37.   Time _t;
    38. };

    2、友元关系不能传递(C是B的友元,B是A的友元,在未声明的情况下C不是A的友元)

    3、友元关系不能继承

    4、友元关系是双向时,两个类都可以访问对方的成员变量和成员函数

    内部类

    基本概念:一个类定义在另一个类的内部,这个内部类就叫内部类

    注意事项:

    1、类不占用空间(编译后不占用空间)

    1. #include
    2. using namespace std;
    3. class A
    4. {
    5. public:
    6. class B
    7. {
    8. private:
    9. int _b1;
    10. };
    11. private:
    12. int _a1;
    13. int _a2;
    14. };
    15. int main()
    16. {
    17. cout << sizeof(A)<
    18. return 0;
    19. }

    想象中在A类中嵌套一个B类应该是下的代码是这样的,但实际上不是:

    2、内部类受外部类的类域的限制

    3、内部类是外部类的友元(内部类可以访问外部类的成员,但是外部类不能访问内部类的成员)

    4、内部类可以定义在外部类的任意位置

    5、内部类可以直接访问外部类中的static成员,不需要外部类的对象或类名

    1. #include
    2. using namespace std;
    3. class A
    4. {
    5. private:
    6. static int k;
    7. int h;
    8. public:
    9. class B // B天生就是A的友元
    10. {
    11. public:
    12. void foo(const A& a)
    13. {
    14. cout << k << endl;//OK,this->k
    15. cout << a.h << endl;//OK,this->h
    16. }
    17. };
    18. };
    19. int A::k = 1;
    20. int main()
    21. {
    22.    A::B b;
    23.    b.foo(A());
    24.    return 0;
    25. }
    • A() :匿名的临时对象
    • b.foo(A()): 将匿名对象作为参数传递给了类 A 中嵌套类 B 的成员函数 foo()

    6、sizeof(外部类) = 外部类的大小,和内部类没有任何关系

    匿名对象

    格式:类名()

    注意事项:

    1、匿名对象的括号内可以有参数

    2、匿名对象的声明周期只在当前一行(第1行定义匿名对象,第2行时该匿名对象销毁)

    1. #include
    2. using namespace std;
    3. class A
    4. {
    5. public:
    6. A(int a = 0)
    7. :_a(a)
    8. {
    9. cout << "A(int a)" << endl;
    10. }
    11. ~A()
    12. {
    13. cout << "~A()" << endl;
    14. }
    15. private:
    16. int _a;
    17. };
    18. class Solution
    19. {
    20. public:
    21. int Sum_Solution(int n)
    22. {
    23. //...
    24. return n;
    25. }
    26. };
    27. int main()
    28. {
    29. A aa1;//有名对象
    30. A();//匿名对象
    31. A(10);//匿名对象
    32. A aa2(2);//有名对象
    33. Solution().Sum_Solution(10);
    34. return 0;
    35. }

    3、匿名对象可以提供一些便利(当我们只是向调用对象的内容时有名写两行,匿名写一行)

    1. //形式一
    2. Solution s1;
    3. s1.func(10);
    4. //形式二
    5. Solution().Sum_Solution(10);

    拷贝对象时的一些编译器优化

    基本概念:在传参和传返回值时,一般编译器会做一些优化,减少对象的拷贝

    连续构造 + 拷贝构造 = 优化为直接构造

    连续构造 + 拷贝构造 = 优化为一个构造

    连续拷贝构造 + 拷贝构造 = 优化为一个拷贝构造

    连续拷贝构造 + 赋值重载 = 无法优化

    再次理解类和对象

            计算机不认识现实生活中的实体,只认识二进制格式的数据,如果想要计算机认识现实中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识,比如像要让计算机认识洗衣机就需要:

    1. 用户先对现实中的洗衣机实体进行抽象认知,即在思想层面对洗衣机进行认识,洗衣机有什么属性(成员变量)和功能(成员函数)
    2. 此时,人脑中已经对洗衣机有了一个较为清醒的认识,通过某种面相对象的语言将洗衣机用类来进行描述,就可以让计算机知道人脑中对洗衣机的认识
    3. 然后,在计算机中就有了一个洗衣机类,但它只是站在计算机的角度对洗衣机进行描述的,只有利用洗衣机类实例化出具体的洗衣机对象,用户才可以模拟现实中洗衣机实体的功能
    1. #include
    2. #include
    3. class WashingMachine {
    4. private:
    5. int capacity; // 洗衣容量
    6. bool isOn; // 洗衣机是否开启
    7. std::string brand; // 品牌
    8. public:
    9. WashingMachine(int cap, const std::string& b) : capacity(cap), isOn(false), brand(b) {}
    10. void turnOn() {
    11. isOn = true;
    12. std::cout << "Washing machine turned on." << std::endl;
    13. }
    14. void turnOff() {
    15. isOn = false;
    16. std::cout << "Washing machine turned off." << std::endl;
    17. }
    18. void washClothes() {
    19. if(isOn) {
    20. std::cout << "Washing clothes..." << std::endl;
    21. } else {
    22. std::cout << "Please turn on the washing machine first."<
    23. }
    24. }
    25. };
    26. int main() {
    27. WashingMachine myWasher(5, "ABC");
    28. myWasher.turnOn();
    29. myWasher.washClothes();
    30. return 0;
    31. }

    ~over~

  • 相关阅读:
    11.1.0- iDesktopX新特性之统计面内对象数
    【WinSCP】强大的可视化远程文件传输 、管理工具 (支持多种协议,支持电脑与手机)
    git操作说明
    使用 Python 的高效相机流
    【付费推广】常见问题合集,搜索推广FAQ 1
    SSH连接WSL2踩坑记录与增加端口转换规则,实现外网与WSL2的连接
    Python selenium 页面滚动
    AlphaFold-multimer 复合物结构预测
    代码随想录算法训练营20期|第五十七天|动态规划part15|● 392.判断子序列 ● 115.不同的子序列
    基于开源库libreDWG+Java实现AutoCad格式DWG转DXF
  • 原文地址:https://blog.csdn.net/m0_73975164/article/details/136638221