• (c++)类和对象 下篇


    目录

    1.再次了解构造函数

    2. Static成员

    3. 友元

    4. 内部类

    5.匿名对象

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


    1.再次了解构造函数

    1.1 构造函数体赋值
    •    在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。
    1. class Date
    2. {
    3. public:
    4. Date(int year, int month, int day)
    5. {
    6.     _year = year;
    7.     _month = month;
    8.     _day = day;
    9. }
    10. private:
    11. int _year;
    12. int _month;
    13. int _day;
    14. };
            虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值 ,而不能称作初始化。因为 初始化只能初始 化一次,而构造函数体内可以多次赋值

    所以这里抛出了一个问题,对象成员变量在哪里初始化/定义?

    1.2 初始化列表
    初始化列表:以一个 冒号开始 ,接着是一个以 逗号分隔的数据成员列表 ,每个 " 成员变量 " 后面跟一个放在括号中的初始值或表达式。

    【注意】
    1. 每个成员变量在初始化列表中 只能出现一次 ( 初始化只能初始化一次 )
    2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
    • 引用成员变量
    • const成员变量
    • 自定义类型成员(且该类没有默认构造函数时)
    3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。

    4. 成员变量 在类中 声明次序 就是其在初始化列表中的 初始化顺序 ,与其在初始化列表中的先后次序无关

    例:

    1. class A
    2. {
    3. public:
    4.    A(int a)
    5.       :_a1(a)
    6.       ,_a2(_a1)
    7.   {}
    8.    
    9.    void Print() {
    10.        cout<<_a1<<" "<<_a2<<endl;
    11.   }
    12. private:
    13.    int _a2;
    14.    int _a1;
    15. };
    16. int main()
    17. {
    18.    A aa(1);
    19.    aa.Print();
    20. }

    运行结果:

    为什么?因为上述代码是_a2首先声明,所以先初始化_a2,但是此时_a1是随机值

    1.3 explicit 关键字
         构造函数不仅可以构造与初始化对象, 对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用
    1. class Date
    2. {
    3. public:
    4. // 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用
    5. // explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译
    6. explicit Date(int year)
    7. :_year(year)
    8. {}
    9. /*
    10. // 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具
    11. 有类型转换作用
    12. // explicit修饰构造函数,禁止类型转换
    13. explicit Date(int year, int month = 1, int day = 1)
    14. : _year(year)
    15. , _month(month)
    16. , _day(day)
    17. {}
    18. */
    19. Date& operator=(const Date& d)
    20. {
    21. if (this != &d)
    22. {
    23. _year = d._year;
    24. _month = d._month;
    25. _day = d._day;
    26. }
    27. return *this;
    28. }
    29. private:
    30. int _year;
    31. int _month;
    32. int _day;
    33. };
    34. void Test()
    35. {
    36. Date d1(2022);
    37. // 用一个整形变量给日期类型对象赋值
    38. // 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值
    39. d1 = 2023;
    40. //1屏蔽掉,2放开时则编译失败,因为explicit修饰构造函数,禁止了单参构造函数类型转
    41. 换的作用
    42. }
    上述代码可读性不是很好, explicit 修饰构造函数,将会禁止构造函数的隐式转换

    2. Static成员

    2.1 概念
    声明为 static 的类成员 称为 类的静态成员 ,用 static 修饰的 成员变量 ,称之为 静态成员变量 ;用 static 修饰 成员函数 ,称之为 静态成员函数 静态成员变量一定要在类外进行初始化
    面试题:实现一个类,计算程序中创建出了多少个类对象。
    常用方法:
    1. int count = 0;
    2. class A
    3. {
    4. public:
    5. A(int a = 1)
    6. {
    7. ++count;
    8. }
    9. A(const A& aa)
    10. {
    11. ++count;
    12. }
    13. };
    14. void Func(A a)
    15. {}
    16. int main()
    17. {
    18. A aa1;
    19. A aa2(aa1);
    20. Func(aa1);
    21. A aa3 = 1;
    22. cout << count << endl;
    23. return 0;
    24. }

    使用static成员:

    注:
    2.2 特性
    1. 静态成员 所有类对象所共享 ,不属于某个具体的对象,存放在静态区
    2. 静态成员变量 必须在 类外定义 ,定义时不添加 static 关键字,类中只是声明
    3. 类静态成员即可用 类名 :: 静态成员 或者 对象 . 静态成员 来访问
    4. 静态成员函数 没有 隐藏的 this 指针 ,不能访问任何非静态成员
    5. 静态成员也是类的成员,受 public protected private 访问限定符的限制


    3. 友元

            友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
    • 友元分为:友元函数友元类
    3.1 友元函数
            问题:现在尝试去重载 operator<< ,然后发现没办法将 operator<< 重载成成员函数。 因为 cout 输出流对象和隐含的 this 指针在抢占第一个参数的位置 this 指针默认是第一个参数也就是左操作数了。但是实际使用中cout 需要是第一个形参对象,才能正常使用。所以要将 operator<< 重载成全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>> 同理。

    1. class Date
    2. {
    3. public:
    4. Date(int year, int month, int day)
    5.     : _year(year)
    6.     , _month(month)
    7.     , _day(day)
    8. {}
    9. // d1 << cout; -> d1.operator<<(&d1, cout); 不符合常规调用
    10. // 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧
    11. ostream& operator<<(ostream& _cout)
    12. {
    13.     _cout << _year << "-" << _month << "-" << _day << endl;
    14.     return _cout;
    15. }
    16. private:
    17. int _year;
    18. int _month;
    19. int _day;
    20. };
            友元函数 可以 直接访问 类的 私有 成员,它是 定义在类外部 普通函数 ,不属于任何类,但需要在类的内部声明,声明时需要加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. }
    说明 :
    • 友元函数可访问类的私有和保护成员,但不是类的成员函数
    • 友元函数不能用const修饰
    • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
    • 一个函数可以是多个类的友元函数
    • 友元函数的调用与普通函数的调用原理相同
    3.2 友元类

        友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

    • 友元关系是单向的,不具有交换性。
    比如上述 Time 类和 Date 类,在 Time 类中声明 Date 类为其友元类,那么可以在 Date 类中直接
    访问 Time 类的私有成员变量,但想在 Time 类中访问 Date 类中私有的成员变量则不行。
    • 友元关系不能传递
    • 如果CB的友元, BA的友元,则不能说明CA的友元。
    • 友元关系不能继承
    1. class Time
    2. {
    3.   friend class Date;   // 声明日期类为时间类的友元类,则在日期类中就直接访问Time
    4. 中的私有成员变量
    5. public:
    6. Time(int hour = 0, int minute = 0, int second = 0)
    7. : _hour(hour)
    8. , _minute(minute)
    9. , _second(second)
    10. {}
    11.  
    12. private:
    13.   int _hour;
    14.   int _minute;
    15.   int _second;
    16. };
    17. class Date
    18. {
    19. public:
    20.   Date(int year = 1900, int month = 1, int day = 1)
    21.       : _year(year)
    22.       , _month(month)
    23.       , _day(day)
    24.   {}
    25.  
    26.   void SetTimeOfDate(int hour, int minute, int second)
    27.   {
    28.       // 直接访问时间类私有的成员变量
    29.       _t._hour = hour;
    30.       _t._minute = minute;
    31.       _t._second = second;
    32.   }
    33.  
    34. private:
    35.   int _year;
    36.   int _month;
    37.   int _day;
    38.   Time _t;
    39. };


    4. 内部类

          概念: 如果一个类定义在另一个类的内部,这个内部类就叫做内部类 。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
           注意: 内部类就是外部类的友元类 ,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
    特性:
    • 1. 内部类可以定义在外部类的publicprotectedprivate都是可以的。
    • 2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
    • 3. sizeof(外部类)=外部类,和内部类没有任何关系。

    1. class A
    2. {
    3. private:
    4. static int k;
    5. int h;
    6. public:
    7. class B // B天生就是A的友元
    8. {
    9. public:
    10. void foo(const A& a)
    11. {
    12. cout << k << endl;//OK
    13. cout << a.h << endl;//OK
    14. }
    15. };
    16. };
    17. int A::k = 1;
    18. int main()
    19. {
    20.    A::B b;
    21.    b.foo(A());
    22.    
    23.    return 0;
    24. }


    5.匿名对象

    1. class A
    2. {
    3. public:
    4. A(int a = 0)
    5. :_a(a)
    6. {
    7. cout << "A(int a)" << endl;
    8. }
    9. ~A()
    10. {
    11. cout << "~A()" << endl;
    12. }
    13. private:
    14. int _a;
    15. };
    16. class Solution
    17. {
    18. public:
    19. int Sum_Solution(int n)
    20. {
    21. //...
    22. return n;
    23. }
    24. };
    25. int main()
    26. {
    27. A aa1;
    28. //A aa1();
    29. // 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义
    30. // 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
    31. // 但是他的生命周期只有这一行,下一行它就会自动调用析构函数
    32. A();
    33. Solution();
    34. // 使用场景
    35. cout << Solution().Sum_Solution(10) << endl;
    36. return 0;
    37. }


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

    1. class A
    2. {
    3. public:
    4. //构造
    5. A(int a = 0)
    6. :_a(a)
    7. {
    8. cout << "A(int a)" << endl;
    9. }
    10. //拷贝构造
    11. A(const A& aa)
    12. :_a(aa._a)
    13. {
    14. cout << "A(const A& aa)" << endl;
    15. }
    16. //赋值
    17. A& operator=(const A& aa)
    18. {
    19. cout << "A& operator=(const A& aa)" << endl;
    20. if (this != &aa)
    21. {
    22. _a = aa._a;
    23. }
    24. return *this;
    25. }
    26. //析构
    27. ~A()
    28. {
    29. cout << "~A()" << endl;
    30. }
    31. private:
    32. int _a;
    33. };

         


    注:概念性内容均来自比特科技

  • 相关阅读:
    [ C++ ] C++之模板template
    珂朵莉树学习笔记
    echarts 双Y轴折线图
    【C语言】VS实⽤调试技巧&(Debug和Release)监视&内存
    Google Summer of Code谷歌编程之夏活动流程全解析(上)
    存储架构颠覆者Optane持久性内存
    sqli-labs/Less-61
    2022 AI趋势8大预测
    正则表达式(Linux 下搭配 grep 使用)
    Ribbon官网及参考文档
  • 原文地址:https://blog.csdn.net/m0_73969113/article/details/133203907