• 【从小白到大白01】c++类和对象


    目录

    前言

    类的定义

    类的访问限定符及封装

    类的作用域-作用域操作符::

    如何计算类的大小

     (另-先贴一份Date函数在这里,往后篇幅经常用到噢!!!)

    this指针

     (另-先贴一份Stack函数和Queue函数在这里,往后篇幅经常用到噢!!!)

    类的6个默认成员函数

    构造函数:初始化对象

    析构函数:销毁对象

    拷贝构造函数

    赋值运算符重载

    运算符重载

    关键字operator

    关键字operator后面接需要重载的运算符符号

    因此我顺手写了个日期的对比大小的函数......

    总结



    前言

    多多重复,百炼成钢!!!


    类的定义

    class-类的关键字

    classname-类的名字

    {-}内,类的主体,后紧接着分号

    类体中的内容称为类的成员;类内变量称为类的属性或者成员变量;类内函数成为类的方法或者成员函数。

    类的访问限定符及封装

     

    另: class的默认访问权限为private,struct为public(因为struct要兼容C)

    访问限定符只在 编译 时有用,当数据映射到内存后,没有任何访问限定符上的区别

    类的作用域-作用域操作符::

    1. //类的作用域-类定义了一个新的作用域,类的所有成员都在类的作用域中。
    2. //在类体外定义成员时,需要使用 ::
    3. class people
    4. {public:
    5. int findperson();
    6. private:
    7. int son;
    8. int dad;
    9. int mother;
    10. };
    11. //作用域操作符指明成员属于哪个类域。
    12. int people::findperson()//这里的的findperson()属于people这个类域
    13. {
    14. return people::mother;
    15. }

    如何计算类的大小

    空类:编译器给了一个字节- 腾出空间来给该类标识(没料的菜鸟也可以占位!!!)

    只有一个成员函数:编译器也给了一个字节

    有成员变量时:要按照结构体内存对齐规则!!!

    1. 第一个成员在与结构体偏移量为0的地址处。
    2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
    VS中默认的对齐数为8
    3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
    4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
    体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

     (另-先贴一份Date函数在这里,往后篇幅经常用到噢!!!)

    1. class Date
    2. {
    3. public:
    4. void Init(int year, int month, int day)
    5. {
    6. _year = year;
    7. _month = month;
    8. _day = day;
    9. }
    10. void Print()
    11. {
    12. cout << _year << "-" << _month << "-" << _day << endl;
    13. }
    14. private:
    15. int _year;
    16. int _month;
    17. int _day;
    18. };
    19. int main()
    20. {
    21. Date d1, d2;
    22. d1.Init(2022, 9, 23);
    23. d2.Init(2022, 1, 11);
    24. d1.Print();
    25. d2.Print();
    26. return 0;
    27. }

    this指针

    1. this 指针的类型:类类型 * const ,即成员函数中,不能给 this 指针赋值。
    2. 只能在 成员函数 的内部使用
    3. this 指针本质上是 成员函数 的形参 ,当对象调用成员函数时(创建栈帧),将对象地址作为实参传递给 this形参。所以 对象中不存储 this 指针
    4. this 指针是 成员函数 第一个隐含的指针形参,一般情况由编译器通过 ecx 寄存器自动传
    递,不需要用户传递

    根据以下代码:

    我们创建了一个Date类

    定义了一个d1和d2并且都初始化

    1.d1和d2调用的是同一个Print()函数-在公共区

    2.c++编译器通过给每(非静态成员函数)一个this指针(隐藏的),让该指针指向当前对象(调用公共区函数的对象)-把对象的地址传给函数

    1. class Date
    2. {
    3. public:
    4. void Init(int year, int month, int day)
    5. {
    6. _year = year;
    7. _month = month;
    8. _day = day;
    9. }
    10. void Print()// 等同于Print(Date*this)-(Date*d1)/(Date*d2)
    11. {
    12. cout << _year << "-" << _month << "-" << _day << endl;
    13. }
    14. private:
    15. int _year;
    16. int _month;
    17. int _day;
    18. };
    19. int main()
    20. {
    21. Date d1;
    22. Date d2;
    23. d1.Init(1988, 11, 29);
    24. d1.Print();
    25. d2.Init(1997, 10, 8);
    26. d2.Print();
    27. return 0;
    28. }

     (另-先贴一份Stack函数和Queue函数在这里,往后篇幅经常用到噢!!!)

    1. typedef int DataType;
    2. class Stack
    3. {
    4. public:
    5. Stack()
    6. {
    7. /* cout << "Queue q调用了自定义类型 Stack的构造函数来初始化" << endl;*/
    8. _array = (DataType*)malloc(sizeof(DataType) * 3);
    9. if (NULL == _array)
    10. {
    11. perror("malloc申请空间失败!!!");
    12. return;
    13. }
    14. _capacity = 3;
    15. _size = 0;
    16. }
    17. ~Stack()
    18. {
    19. cout << "Queue q调用Stack的析构函数" << endl;
    20. free(_array);
    21. _array = NULL;
    22. _capacity = 0;
    23. _size = 0;
    24. }
    25. void Push(DataType data)
    26. {
    27. CheckCapacity();
    28. _array[_size] = data;
    29. _size++;
    30. }
    31. void Pop()
    32. {
    33. if (Empty())
    34. return;
    35. _size--;
    36. }
    37. DataType Top() { return _array[_size - 1]; }
    38. int Empty() { return 0 == _size; }
    39. int Size() { return _size; }
    40. private:
    41. void CheckCapacity()
    42. {
    43. if (_size == _capacity)
    44. {
    45. int newcapacity = _capacity * 2;
    46. DataType* temp = (DataType*)realloc(_array, newcapacity *
    47. sizeof(DataType));
    48. if (temp == NULL)
    49. {
    50. perror("realloc申请空间失败!!!");
    51. return;
    52. }
    53. _array = temp;
    54. _capacity = newcapacity;
    55. }
    56. }
    57. private:
    58. DataType* _array;
    59. int _capacity;
    60. int _size;
    61. };
    62. class MyQueue {
    63. public:
    64. void push(int x)
    65. {
    66. _pushST.Push(x);
    67. }
    68. Stack _pushST;
    69. Stack _popST;
    70. private:
    71. int _a;
    72. int _b;
    73. //size_t _size;
    74. };
    75. int main()
    76. {
    77. MyQueue q;
    78. q.push(1);
    79. q.push(2);
    80. /*s.Destroy();*/
    81. return 0;
    82. }

    类的6个默认成员函数

    如果一个类中什么成员都没有,简称为空类。
    空类中真的什么都没有吗???
    并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员 函数。
    默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

    构造函数:初始化对象

    是否会因为每次忘记初始化对象而烦恼?现在它来了!构造函数自动帮你初始化对象!

    1. 函数名与类名相同;创建类类型对象时由编译器自动调用,以保证 每个数据成员都有一个   合适的初始值,并且在对象整个生命周期内只调用一次。
    2. 无返回值。
    3. 对象实例化时编译器自动调用对应的构造函数。

    4. 构造函数可以重载

    5.构造函数可以缺省

     

    1.如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦 用户显式定义编译器将不再生成。
    2.C++编译器把类型分成 内置类型 (语言提供的数据类比如int、char、double等等)和 自定义类型 (我们构造的class、struct、union等等)。
    3.C++编译器对 内置类型 不初始化(给随机值),对 自定义类型 会调用该自定义类型的构造函数。
    我们可以看到c++编译器对内置类型不初始化(给随机值)

    然而:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁

    即:内置类型成员变量在类中声明时可以给默认值-给的是缺省值

    在这里我们可以看到c++编译器对自定义类型会调用该函数的构造函数来初始化

    无参构造函数全缺省构造函数都称为默认构造函数,并且默认构造函数只能有一个。
    另:无参构造函数全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为
    是默认构造函数。
    若Queue q调用Stack的默认构造函数时,此时Stack没有无参数构造函数、全缺省构造函数,则会调用编译器默认生成的构造函数(里面给的内置参数为随机值)。
    解决办法:在内置参数处设置缺省值(初始化)。

    1. typedef int DataType;
    2. class Stack
    3. {
    4. public:
    5. //Stack()
    6. //{
    7. ///* cout << "Queue q调用了自定义类型 Stack的构造函数来初始化" << endl;*/
    8. // _array = (DataType*)malloc(sizeof(DataType) * 3);
    9. // if (NULL == _array)
    10. // {
    11. // perror("malloc申请空间失败!!!");
    12. // return;
    13. // }
    14. // _capacity = 3;
    15. // _size = 0;
    16. //}
    17. //~Stack()
    18. //{
    19. // cout << "Queue q调用Stack的析构函数" << endl;
    20. // free(_array);
    21. // _array = NULL;
    22. // _capacity = 0;
    23. // _size = 0;
    24. //}
    25. void Push(DataType data)
    26. {
    27. CheckCapacity();
    28. _array[_size] = data;
    29. _size++;
    30. }
    31. void Pop()
    32. {
    33. if (Empty())
    34. return;
    35. _size--;
    36. }
    37. DataType Top() { return _array[_size - 1]; }
    38. int Empty() { return 0 == _size; }
    39. int Size() { return _size; }
    40. private:
    41. void CheckCapacity()
    42. {
    43. if (_size == _capacity)
    44. {
    45. int newcapacity = _capacity * 2;
    46. DataType* temp = (DataType*)realloc(_array, newcapacity *
    47. sizeof(DataType));
    48. if (temp == NULL)
    49. {
    50. perror("realloc申请空间失败!!!");
    51. return;
    52. }
    53. _array = temp;
    54. _capacity = newcapacity;
    55. }
    56. }
    57. private:
    58. DataType* _array;
    59. int _capacity=1;
    60. int _size=1;
    61. };
    62. class MyQueue {
    63. public:
    64. void push(int x)
    65. {
    66. cout << "调用默认Stack构造函数完成push" << endl;
    67. _pushST.Push(x);
    68. }
    69. Stack _pushST;
    70. Stack _popST;
    71. private:
    72. int _a = 0;
    73. int _b = 0;
    74. //size_t _size;
    75. };
    76. int main()
    77. {
    78. MyQueue q;
    79. q.push(1);
    80. /*s.Destroy();*/
    81. return 0;
    82. }

    析构函数:销毁对象

    是否常常会因为忘记销毁对象而烦恼?是否会因为总是要在结尾处把对象free掉而烦恼?

    不用担心!现在它来了!析构函数自动帮你销毁对象!

    1. 析构函数名是在类名前加上字符 ~。
    2. 无参数无返回值类型。
    3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载
    4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
    (另:有三种销毁对象情况:
       第一种是局部变量->在局部函数(栈帧)销毁时一齐销毁
       第二种是全局变量->在全局函数结束时销毁
       第三种是类malloc函数->要主动去free销毁 (是这种情况时最好写析构函数)
    下面的程序我们会看到,编译器 生成的默认析构函数,对自定类型成员调用它的析构函数

    拷贝构造函数

    1.只有单个形参,该形参是对本类类型对象的 引用 (一般常用 const 修饰),在用已存
    在的类类型对象创建新对象时由编译器自动调用。
    2. 拷贝构造函数是构造函数的一个 重载 形式。
    3.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。
    4.拷贝函数使用场景:
    使用已存在对象构建新对象、返回值类型为已存在类对象、函数参数类型为已存在类对象

    1. class Date
    2. {
    3. public:
    4. Date(int year=1, int month=1, int day=1)//构造函数-自动调用
    5. {
    6. cout << "调用构造函数" << endl;
    7. _year = year;
    8. _month = month;
    9. _day = day;
    10. }
    11. ~Date()
    12. {
    13. cout << "自动调用析构函数" << endl;
    14. _year = 0;
    15. _month = 0;
    16. _day = 0;
    17. }
    18. Date(const Date& d)//拷贝构造函数
    19. {
    20. cout<< "调用拷贝构造" << endl;
    21. _year = d._year;
    22. _month = d._month;
    23. _day = d._day;
    24. }
    25. void Print()
    26. {
    27. cout << _year << "-" << _month << "-" << _day << endl;
    28. }
    29. private:
    30. int _year;
    31. int _month;
    32. int _day;
    33. };
    34. int main()
    35. {
    36. Date d1(1988, 3, 23);
    37. Date d2(d1);
    38. d2.Print();
    39. return 0;
    40. }

    若未显式定义,编译器会生成默认的拷贝构造函数。
    1.默认的拷贝构造函数对象按内存存储按 字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
    2.若类中涉及到要资源申请的-必须要写拷贝构造函数(如:链表、二叉树、栈、队列等等)
    总结成两句话:
    需要写析构函数的类-就需要写拷贝构造函数;(比如队列调用栈、栈调用队列等等)
    不需要写析构函数的类-使用编译器默认生成的拷贝构造就🆗

     浅拷贝可能引发的问题:

    (1)同一块资源被释放两次;

    (2)后一个类通过拷贝构造函数在同一块空间改变前一个类(打个比方:st2通过st1前往公园的路反向找到st1的家并且。。。细想是不是很恐怖!)

    所以我们要写拷贝构造函数让st2也拥有自己的一块内存

    赋值运算符重载

    运算符重载

    关键字operator

    关键字operator后面接需要重载的运算符符号

    函数原型:返回值类型 operator操作符(参数列表)

    另:
    1.不能通过连接其他符号来创建新的操作符:比如operator@
    2.重载操作符必须有一个类类型参数(参数针对自定义类型不能是内置类型:int、char...)
    3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
    4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
    藏的this指针
    5.【 .* 、 ::、  sizeof、  ?:、 .  】 注意以上5个运算符不能重载。这个要记住!!!

     当运算符重载成全局时就需要调用的参数为类中公有的;但假如我们在实现时需要调用类中私有的参数呢?

    我们就可以把operator放到类中这样就不仅可以调用公有的参数也可以调用私有的参数!

    因此我顺手写了个日期的对比大小的函数......

    日期对比大小的函数:代码我贴这了!(0为假1为真)

    1. #include
    2. using namespace std;
    3. class Date
    4. {
    5. public:
    6. Date(int year=1, int month=1, int day=1)//构造函数-自动调用
    7. {
    8. _year = year;
    9. _month = month;
    10. _day = day;
    11. }
    12. ~Date()
    13. {
    14. _year = 0;
    15. _month = 0;
    16. _day = 0;
    17. }
    18. bool operator==(const Date& d)
    19. {
    20. return _year == d._year
    21. && _month == d._month
    22. && _day == d._day;
    23. }
    24. bool operator>(const Date& d)
    25. {
    26. if (_year > d._year)
    27. {
    28. return true;
    29. }
    30. else if (_year == d._year && _month > d._month)
    31. {
    32. return true;
    33. }
    34. else if (_year == d._year && _month == d._month && _day > d._day)
    35. {
    36. return true;
    37. }
    38. return false;
    39. }
    40. bool operator>=(const Date& d)
    41. {
    42. return *this > d|| *this==d;
    43. }
    44. bool operator<(const Date& d)
    45. {
    46. return 1 ^ (*this > d);
    47. }
    48. void Print()
    49. {
    50. cout << _year << "-" << _month << "-" << _day << endl;
    51. }
    52. private:
    53. int _year;
    54. int _month;
    55. int _day;
    56. };
    57. int main()
    58. {
    59. Date d1(1988, 3, 23);
    60. Date d2(2020,9,24);
    61. cout<<(d1 == d2)<
    62. cout << (d1 > d2) << endl;
    63. cout << (d1 < d2) << endl;
    64. return 0;
    65. }

    总结

    c++中类和对象内容颇多,以上内容后续会持续更新噢~~~

    以上内容如果对你有帮助的话,不妨点赞收藏走一波~~~

    有任何问题都可以在评论区提出来噢~~~

  • 相关阅读:
    电子学:第010课——实验 8:继电振荡器
    微信小程序实战十四:小程序及APP端实现客服功能
    Linux下搭建nginx服务器
    一篇前段时间使用评分卡的总结_20231022
    【slam十四讲第二版】【课后习题】【第九讲~后端1】
    【JavaSE】JavaSE之方法的使用
    Espent环境配置与实践
    QML粒子系统
    实战三十一:基于LightGCN推荐算法的推荐系统详细教程代码+数据
    杰理之增加自动mute处理节点【篇】
  • 原文地址:https://blog.csdn.net/m0_71841506/article/details/127001352