C++入门:类和对象(入门篇)

[复制链接]
发表于 2026-2-8 20:44:39 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
目次
媒介
类的界说
   1.类界说格式
    2.从结构体到类的高出
    3.访问限定符
    4.类域 
    5.类的实例化
类的默认成员函数
     1.默认成员函数的界说和学习方向
      2.构造函数 
      3.析构函数
      4.拷贝构造函数
      5.重载运算符
      总结
疑难明答
    1.this指针的用法
    2.为什么拷贝构造函数的第一个参数必须是类范例对象的引用?
    3.函数返回引用的注意事项
    4.关于‘++’‘--’运算符的前置后置重载


媒介

        对于 C++的初学者而言,大概会心生狐疑:明显结构体就能搞定的事故,为何还要弄出个“类”来?而且要是数据能像在结构体中那样随时随地随意使用,岂不是方便至极?进而得出“C++不如 C 语言”的结论。
        在此,我必须为 C++叫屈!咱们 C++的祖师爷绝不大概闲来无事创造一门语言。何况,既然C++能被广大步调员广泛学习和运用,必有其过人之处!C 语言在应对大规模、复杂的步调时存在肯定范围性。而类和对象这一概念的引入,为步调员提供了更为强盛的工具,用于构造和管理代码,能更出色地模拟实际天下中的题目与实体,实现更高程度的代码复用、封装、继承和多态等特性。(这里并非要表明 C++比 C 语言更出色,二者各有千秋。C 语言面向过程,运行速率更快,对内存有着至高权限;C++面向对象,在处理处罚复杂对象时,上风尽显。)
        C++的学习之路肯定是崎岖的,它的概念和语法相较于其他语言大概会显得更为复杂和抽象。但请信赖,每一次攻克困难后的劳绩都将是无比宝贵的。当你深入明确并纯熟运用类和对象的概念时,你会发现它为编程带来的巨大便利和服从提升。
        以是,盼望各人不要轻言放弃。接下来,我会领导各人从结构体渐渐过渡到类,尽大概让各人渐渐体悟二者的差异。闲话少叙,咱们立刻进入正题。
类的界说

   1.类界说格式

        class为界说类的关键字,Point为类的名字(以本文为例,名字可自界说),{}中为类的主体,注意类界说竣事时背面分号不能省略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的⽅法大概成员函数。
        我们来看一个例子:
  1. class Point
  2. {
  3.     private:
  4.         // 私有成员变量 x,用于存储点的 x 坐标
  5.         int x;
  6.         // 私有成员变量 y,用于存储点的 y 坐标
  7.         int y;
  8.     public:
  9.         // 公共成员函数,用于设置点的坐标
  10.         void set(int x, int y)
  11.         {
  12.             // 使用 this 指针区分成员变量和参数
  13.             this->x = x;
  14.             this->y = y;
  15.         }
  16.         // 公共成员函数,用于打印点的坐标
  17.         void print()
  18.         {
  19.             std::cout << "Point: (" << x << ", " << y << ")" << std::endl;
  20.         }
  21. };
复制代码
     2.从结构体到类的高出

          呀~这不就是C语言里的结构体嘛,老朋侪了!
  

        错!这是本日要带你认识的新年老!也是C和C++在本质上一个很大的差别之处。可以说C++被创造出来,有很大一部门缘故起因就是由于类。
        由于C 语言在处理处罚大规模、复杂的步调时存在一些范围性。而类和对象的概念引入为步调员提供了更强盛的工具来构造和管理代码,更好地模拟实际天下中的题目和实体,实现更高程度的代码复用、封装、继承和多态等特性。 
        下面我们来先容C++中的类和C语言中的结构体的异同:
   C++ 中的类和 C 语言中的结构体有以下雷同点:
  

  • 都可以包罗差别数据范例的成员:可以有整型、浮点型、字符型、指针等各种范例的成员变量。
  • 成员的存储方式雷同:按照声明的序次依次存储在内存中。
  • 都可以作为函数的参数举行通报:可以按值通报大概通过指针通报。
它们的差别点重要有:
  

  • 成员的默认访问权限差别:在 C 语言的结构体中,成员的默认访问权限是公共的(public);而在 C++ 的类中,成员的默认访问权限是私有的(private)。
  • 继承特性:C++ 的类支持继承机制,可以从一个已有的类派生出新的类,实当代码的复用和扩展;C 语言的结构体没有继承的概念。
  • 多态性:C++ 的类支持多态性,通过虚函数可以实现运行时的动态绑定;C 语言的结构体不支持多态。
  • 函数作为成员:在 C 语言的结构体中,一样平常不能直接包罗函数;而在 C++ 的类中,可以将函数作为成员函数来界说和使用。
  • 封装性:C++ 的类提供了更好的封装性,可以将成员变量和成员函数举行访问权限的控制,埋伏内部实现细节;C 语言的结构体封装性较弱,成员都是公开可访问的。
      我们来上代码了解:
        这是C语言版的,用来实现坐标设置的代码:
  1. #include <stdio.h>
  2. // 定义结构体
  3. typedef struct Point {
  4.     int x;
  5.     int y;
  6. } Point;
  7. // 函数来设置点的坐标
  8. void setPoint(Point *p, int x, int y) {
  9.     p->x = x;
  10.     p->y = y;
  11. }
  12. int main() {
  13.     Point p;
  14.     setPoint(&p, 10, 20);
  15.     printf("Point: (%d, %d)\n", p.x, p.y);
  16.     return 0;
  17. }
复制代码
      下面是C++版本: 
         private和public看不懂的同砚不要急,我们下面就讲。
  1. #include <iostream>
  2. class Point {
  3. //私有
  4. private:
  5.     int x;
  6.     int y;
  7. //公有
  8. public:
  9.     void set(int x, int y) {
  10.         this->x = x;
  11.         this->y = y;
  12.     }
  13.     void print() {
  14.         std::cout << "Point: (" << x << ", " << y << ")" << std::endl;
  15.     }
  16. };
  17. int main() {
  18.     Point p;
  19.     p.set(10, 20);
  20.     p.print();
  21.     return 0;
  22. }
复制代码
        屏幕前的你大概正在心田吐槽:明显就是C语言的代码简便明确。没错,你还真讲对了!(我前面就说了,各有所长)面对对象编程,顾名思义,对象越复杂就越能表现它的奥妙。我们来举个形象的例子:
        在做小项目时,用到的成员变量也不多,就像是给了你图中的方块。你脑海中天然就构建出了雷同村民住所的小房子,乃至你本身盖的更美丽(代码更简便),给你图纸(类)可谓是多此一举,但假如项目过大呢?

         假如给你了一堆变量(方块),却没有给你图纸(类)。肯定是有人能做出来,但大概不如给你图纸那般顺畅罢了。固然这只是个比喻,你可不能由于喜欢MC中本身开动想象力创造,就不喜欢C++哦!

        C++中数据和函数都放到了类⾥⾯,通过访问限定符进⾏了限定,不能再随意通过对象直接修改数 据,这是C++封装的⼀种表现,这个是最紧张的厘革。这⾥的封装的本质是⼀种更严格规范的管 理,克制出现乱访问修改的题目。固然封装不但仅是如许的,我们后⾯还须要不停的去学习。 
        值得一提的是,struct在C++中升级成类了,也可以包罗成员函数。但这并不意味着struct和class就完全一样,他们成员的默认访问权限差别:在struct中,成员的默认访问权限是public(公有);在class中,成员的默认访问权限是private(私有)。这里照旧保举各人去用class。
    3.访问限定符


           C++⼀种实现封装的方式,用类将对象的属性与方法联合在⼀块,让对象更加美满,通过访问权限选择性的将其接口提供给外部的用户使⽤。
  

  • public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访 问,protected和private是⼀样的,以后继承章节才华表现出他们的区别。
  • 访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,假如后⾯没有 访问限定符,作⽤域就到 }即类竣事。
  • class界说成员没有被访问限定符修饰时默以为private,struct默以为public。
  • ⼀般成员变量都会被限定为private/protected,须要给别⼈使⽤的成员函数会放为public。
    4.类域 

        类界说了⼀个新的作⽤域,类的全部成员都在类的作⽤域中,在类体外界说成员时,须要使⽤ :: 作 ⽤域操纵符指明成员属于哪个类域。
        类域影响的是编译的查找规则,下⾯步调中Init假如不指定类域Stack,那么编译器就把Init当玉成 局函数,那么编译时,找不到array等成员的声明/界说在哪⾥,就会报错。指定类域Stack,就是知道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。
        比方:
  1. #include<iostream>
  2. using namespace std;
  3. class Stack
  4. {
  5.     public:
  6.     // 成员函数
  7.     void Init(int n = 4);
  8.     private:
  9.     // 成员变量
  10.     int* array;
  11.     size_t capacity;
  12.     size_t top;
  13. };
  14.     // 声明和定义分离,需要指定类域
  15. void Stack::Init(int n)
  16. {
  17.     array = (int*)malloc(sizeof(int) * n);
  18.     if (nullptr == array)
  19.     {
  20.         perror("malloc申请空间失败");
  21.         return;
  22.     }
  23.     capacity = n;
  24.     top = 0;
  25. }
  26. int main()
  27. {
  28.     Stack st;
  29.     st.Init();
  30.     return 0;
  31. }
复制代码
     5.类的实例化

        用类的范例在物理内存中创建对象的过程,称为类实例化出对象。 
        类是对象举行一种抽象形貌,是⼀个模子⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,用类实例化出对象时,才会分配空间。
        ⼀个类可以实例化出多个对象,实例化出的对象占⽤实际的物理空间,存储类成员变量。打个比方:类实例化出对象就像实际中使⽤修创建计图制作出房子,类就像是操持图,操持图规划了有多 少个房间,房间巨细功能等,但是并没有实体的修建存在,也不能住人,用操持图构筑出房子,房 子才华住人。同样类就像操持图一样,不能存储数据,实例化出的对象分配物理内存存储数据。

  1. #include<iostream>
  2. using namespace std;
  3. class Date
  4. {
  5.         public:
  6.         void Init(int year, int month, int day)
  7.         {
  8.                 _year = year;
  9.                 _month = month;
  10.                 _day = day;
  11.         }
  12.         void Print()
  13.         {
  14.                 cout << _year << "/" << _month << "/" << _day << endl;
  15.         }
  16.         private:
  17.         // 这里只是声明,没有开空间
  18.         int _year;
  19.         int _month;
  20.         int _day;
  21. };
  22. int main()
  23. {
  24.         // Date类实例化出对象d1和d2
  25.     //这里才是占用了空间
  26.         Date d1;
  27.         Date d2;
  28.         d1.Init(2024, 3, 31);
  29.         d1.Print();
  30.         d2.Init(2024, 7, 5);
  31.         d2.Print();
  32.         return 0;
  33. }
复制代码
类的默认成员函数

     1.默认成员函数的界说和学习方向

           默认成员函数就是用户没有显式实现,编译器会自动天生的成员函数称为默认成员函数(但并不是编译器会自动天生我们就可以不学习,反之这恰恰代表了这些函数是必不可少的,很多时间编译器并不能相识和满意我们的意图,此时就须要我们本身动手去写)。
            一个类,我们不写的环境下编译器会默认天生以下6个默认成员函数,须要注意的是这6个中最紧张的是前4个,末了两个取地点重载不紧张,我们稍微相识一下即可。其次就是C++11以后还会增长两个默认成员函数, 移动构造和移动赋值,这个我们背面再解说。默认成员函数很紧张,也比力复杂,我们要从两个方面去学习:
  

  • 第⼀:我们不写时,编译器默认天生的函数举动是什么,是否满意我们的需求。
  • 第⼆:编译器默认天生的函数不满意我们的需求时,我们须要本身实现,那么怎样本身实现?

      2.构造函数 

           构造函数是特殊的成员函数,用于在创建对象时举行初始化操纵。
          须要注意的是,构造函数固然名称叫构造,但是构造函数的重要任务并不是开空间创建对象(我们常使用的局部对象是栈帧创建时,空间就开好了),而是对象实例化时初始化对象。构造函数的本质是要更换我们从前写的Init函数(初始化函数)的功能,构造函数自动调用的特点就完善的更换的了Init。
   构造函数的特点:
  

  • 函数名与类名雷同。
  • 无返回值。 (返回值啥都不须要给,也不须要写void,不要纠结,C++规定云云)
  • 对象实例化时体系会自动调用对应的构造函数。
  • 构造函数可以重载。
  • 假如类中没有显式界说构造函数,则C++编译器会自动天生一个无参的默认构造函数,一旦用户显式界说编译器将不再天生。
  • 无参构造函数、全缺省构造函数、我们不写构造时编译器默认天生的构造函数,都叫做默认构造函数。但是这三个函数有且只有一个存在,不能同时存在。无参构造函数和全缺省构造函数固然构成函数重载,但是调用时会存在歧义。要注意很多同砚会以为默认构造函数是编译器默天生谁人叫默认构造,实际上无参构造函数、全缺省构造函数也是默认构造,总结⼀下就是不传实参就可以调用的构作育叫默认构造。
  • 我们不写,编译器默认天生的构造,对内置范例成员变量的初始化没有要求,也就是说是是否初始化是不确定的,看编译器。对于自界说范例成员变量,要求调用这个成员变量的默认构造函数初始化。假如这个成员变量,没有默认构造函数,那么就会报错,我们要初始化这个成员变量,须要用初始化列表才华办理,初始化列表,我们下个章节再细细解说。(分析:C++把范例分成内置范例(根本范例)和自界说范例。内置范例就是语言提供的原生数据范例, 如:int/char/double/指针等,自界说范例就是我们使用class/struct等关键字本身界说的范例。)
        我们来举个例子。在这个例子中,好好感受我在特点处标黄的三个特点和构造函数的初始化作用。 
  1. #include <iostream>
  2. class Rectangle
  3. {
  4.     private:
  5.         int length;
  6.         int width;
  7.     public:
  8.         // 构造函数,用于初始化矩形的长和宽
  9.         //注意和类名相同,无返回值(什么都没有,连void都没有)
  10.         Rectangle(int l, int w)
  11.         {
  12.             length = l;
  13.             width = w;
  14.         }
  15.     int area()
  16.     {
  17.         return length * width;
  18.     }
  19. };
  20. int main()
  21. {
  22.     Rectangle r(5, 3);  // 使用构造函数创建对象并初始化
  23.     std::cout << "Area: " << r.area() << std::endl;
  24.     return 0;
  25. }
复制代码
      3.析构函数

           析构函数是类的一种特殊成员函数,其作用和目的重要是资源开释。当对象被烧毁时,析构函数负责开释对象在其生命周期内获取的资源,如动态分配的内存、打开的文件、网络毗连等。假如不实时开释这些资源,大概会导致内存走漏或其他资源浪费的题目。
          以是呢,析构函数一样平常在类中包罗动态分配的内存和类使用了其他体系资源,如文件、网络毗连、数据库毗连等环境下才须要我们专门去写。其他时间使用编译器自动天生的析构函数即可。
   析构函数的特点:
  

  • 析构函数名是在类名前加上字符 ~。
  • 无参数无返回值。 (这里跟构造雷同,也不须要加void)
  • 一个类只能有一个析构函数。若未显式界说,体系会自动天生默认的析构函数。
  • 对象生命周期竣事时,体系会自动调用析构函数。
  • 跟构造函数雷同,我们不写编译器自动天生的析构函数对内置范例成员不做处理处罚,自定范例成员会调用他的析构函数。
  • 还须要注意的是我们表现写析构函数,对于自界说范例成员也会调用他的析构,也就是说自界说范例成员无论什么环境都会自动调用析构函数。
  • 假如类中没有申请资源时,析构函数可以不写,直接使用编译器天生的默认析构函数,如;假如默认天生的析构就可以用,也就不须要表现写析构;但是有资源申请时,肯定要本身写析构,否则会造成资源走漏。
  • 一个局部域的多个对象,C++规定后界说的先析构
       下面我们来看一下析构函数在差别场景下的运用:
        1.当类中包罗动态分配的内存:假如在类中分配了内存,那么须要在析构函数中开释,以克制内存走漏。
  1. class example
  2. {
  3.     private:
  4.         int* arr;
  5.     public:
  6.         stack(int size)
  7.         {
  8.             arr = new int[size];
  9.         }
  10.         ~stack()
  11.         {
  12.             delete[] arr;
  13.         }
  14. };
复制代码
        2.当类使用了其他体系资源,如文件、网络毗连、数据库毗连等:在析构函数中关闭或开释这些资源,以确保资源的准确开释和步调的稳固性。
  1. //FileHandler 类用于处理文件操作
  2. class FileHandler
  3. {
  4. private:
  5.     // 用于文件操作的流对象
  6.     std::fstream f;  
  7. public:
  8.     //构造函数,根据给定的文件名打开文件
  9.     //filename 要打开的文件名
  10.     FileHandler(const std::string& filename)
  11.     {
  12.         f.open(filename);
  13.     }
  14.     //析构函数,关闭已打开的文件
  15.     ~FileHandler()
  16.     {
  17.         f.close();
  18.     }
  19. };
复制代码
    4.拷贝构造函数

           假如一个构造函数的第一个参数是自身类范例的引用,且任何额外的参数都有默认值,则此构造函数也叫做拷贝构造函数,也就是说拷贝构造是一个特殊的构造函数。
    拷贝构造的特点:
  

  • 拷贝构造函数是构造函数的一个重载。
  • 拷贝构造函数的第一个参数必须是类范例对象的引用,使用传值方式编译器直接报错,由于语法逻辑上会引发无穷递归调用。 拷贝构造函数也可以多个参数,但是第一个参数必须是类范例对象的引用,背面的参数必须有缺省值。(疑难明答有详讲)
  • C++规定自界说范例对象举行拷贝举动必须调用拷贝构造,以是这里自界说范例传值传参和传值返回都会调用拷贝构造完成。
  • 若未显式界说拷贝构造,编译器会天生自动天生拷贝构造函数。自动天生的拷贝构造对内置范例成员变量会完成值拷贝/浅拷贝(一个字节一个字节的拷贝),对自界说范例成员变量会调用其拷贝构造。
  • 像某些类成员变量满是内置范例且没有指向资源,编译器自动天生的拷贝构作育可以完成所需的拷贝,以是无需我们显式实现拷贝构造。但假如类中有成员指向了资源,编译器自动天生的拷贝构造完成的值拷贝/浅拷贝不符合需求,就须要我们本身实现深拷贝(对指向的资源也举行拷贝)。这里另有一个小本领,假如一个类显式实现了析构并开释资源,那么就须要显式写拷贝构造,否则就不须要。
        举个例子:
  1. #include <iostream>
  2. class MyClass
  3. {
  4.         private:
  5.             int num;
  6.         public:
  7.             // 自定义构造函数
  8.             MyClass(int n)
  9.                 {
  10.                         num=n;
  11.                 }
  12.             // 拷贝构造函数
  13.             MyClass(const MyClass& other)
  14.                 {
  15.                 num = other.num;
  16.                 //拷贝构造函数无返回
  17.             }
  18.        
  19.             void printNum()
  20.                 {
  21.                 std::cout << "当前数字: " << num << std::endl;
  22.             }
  23. };
  24. int main() {
  25.     MyClass num1(798);
  26.     MyClass num2(num1);  // 调用拷贝构造函数
  27.     num1.printNum();
  28.     num2.printNum();
  29.     return 0;
  30. }
复制代码
      5.重载运算符

   重载运算符注意事项:
  

  • 当运算符被用于类范例的对象时,C++语言答应我们通过运算符重载的情势指定新的寄义。C++规定类范例对象使用运算符时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编译报错。
  • 运算符重载是具有特殊名字的函数,他的名字是由operator和背面要界说的运算符共同构成。和其他函数一样,它也具有其返回范例和参数列表以及函数体。
  • 重载运算符函数的参数个数和该运算符作用的运算对象数目一样多。一元运算符有一个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第一个参数,右侧运算对象传给第二个参数。
  • 假如一个重载运算符函数是成员函数,则它的第一个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少一个。
  • 运算符重载以后,其优先级和联合性与对应的内置范例运算符保持同等。不能通过毗连语法中没有的符号来创建新的操纵符:比如operator@。
  • ' .* '     ' :: '     ' sizeof '     ' ?: '     ' . ' 注意以上5个运算符不能重载。(选择题内里常考,各人要记一下)
  • 重载操纵符至少有一个类范例参数,不能通过运算符重载改变内置范例对象的寄义,如: int operator+(int x, int y) • 一个类须要重载哪些运算符,是看哪些运算符重载后故意义,比如Date类重载operator-就故意义,但是重载operator*就没故意义。
  • 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,无法很好的区分。C++规定,后置++重载时,增长一个int形参,跟前置++构成函数重载,方便区分。(疑难明答)
  • 重载 << 和 >> 时,须要重载为全局函数,由于重载为成员函数,this指针默认抢占了第一个形参位置,第一个形参位置是左侧运算对象,调用时就变成了对象<<cout,不符合使用风俗和可读性。重载为全局函数把ostream/istream放到第一个形参位置就可以了,第二个形参位置当类范例对象。
        下面我们来写一个日期类的重载运算符'-'。思绪很简朴,将两个日期分别变整天,然后相减取绝对值(写的时间没预推测日期转换整天的代码还挺长,没有封装,各人将就一下吧):
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int daymaxp[13]={-1,31,28,31,30,31,30,31,31,30,31,30,31};//平年每个月的天数
  4. int daymaxr[13]={-1,31,28,31,30,31,30,31,31,30,31,30,31};//闰年每个月的天数
  5. class Date
  6. {
  7.         public:
  8.                 Date(int a,int b,int c)//构造函数
  9.                 {                               
  10.                         _year=a;
  11.                         _month=b;
  12.                         _day=c;
  13.                 }
  14.                
  15.                 int operator -(Date date)
  16.                 {
  17.                         int day1=0;//将第一个日期换算为天
  18.                         int day2=0;//将第二个日期换算为天
  19.                         for(int i=1;i<=_year;i++)
  20.                         {
  21.                                 if((_year%4==0&&_year%100!=0)||(_year%400==0))//闰年
  22.                                 {
  23.                                         day1+=366;
  24.                                 }
  25.                                 else//平年
  26.                                 {
  27.                                         day1+=365;
  28.                                 }
  29.                         }
  30.                         for(int i=1;i<_month;i++)
  31.                         {
  32.                                 if((_year%4==0&&_year%100!=0)||(_year%400==0))//闰年
  33.                                 {
  34.                                         day1+=daymaxr[i];
  35.                                 }
  36.                                 else
  37.                                 {
  38.                                         day1+=daymaxp[i];
  39.                                 }
  40.                         }
  41.                         day1+=_day;
  42.                         for(int i=1;i<=date._year;i++)
  43.                         {
  44.                                 if((date._year%4==0&&date._year%100!=0)||(date._year%400==0))//闰年
  45.                                 {
  46.                                         day2+=366;
  47.                                 }
  48.                                 else//平年
  49.                                 {
  50.                                         day2+=365;
  51.                                 }
  52.                         }
  53.                         for(int i=1;i<date._month;i++)
  54.                         {
  55.                                 if((date._year%4==0&&date._year%100!=0)||(date._year%400==0))//闰年
  56.                                 {
  57.                                         day2+=daymaxr[i];
  58.                                 }
  59.                                 else
  60.                                 {
  61.                                         day2+=daymaxp[i];
  62.                                 }
  63.                         }
  64.                         day2+=date._day;
  65.                         return (day1-day2)>0?day1-day2:day2-day1;
  66.                 }
  67.                
  68.                 void print()
  69.                 {
  70.                         cout<<_year<<"年"<<_month<<"月"<<_day<<"日";
  71.                 }               
  72.         private:
  73.                 int _year;
  74.                 int _month;
  75.                 int _day;
  76. };
  77. main()
  78. {
  79.         Date day1(2024,8,14);
  80.         Date day2(2024,7,13);
  81.         day2.print();
  82.         cout<<"和";
  83.         day1.print();
  84.         cout<<"相差"<<day2-day1<<"天";//this指针指向‘-’左边的数据
  85. }
复制代码
         运行效果:

总结

   默认成员函数:
        默认成员函数是在类中,假如没有显式界说某些特定函数时,编译器会自动天生的函数。包罗默认构造函数、拷贝构造函数、拷贝赋值运算符、移动构造函数、移动赋值运算符和析构函数。明确它们的默认举动以及何时和怎样自界说这些函数对于准确管理类的对象的创建、复制、烧毁等操纵至关紧张。
  
  构造函数:
        构造函数用于在创建对象时举行初始化工作。可以有多个重载情势,以顺应差别的初始化需求。它确保对象在创建时处于一个有用的初始状态。
  
  析构函数:
        析构函数在对象烧毁时被自动调用,用于开释对象在其生命周期中获取的资源,举行须要的整理工作。
  
  拷贝构造函数:
        拷贝构造函数用于以一个已存在的同范例对象来创建并初始化新对象,实现对象的复制。须要注意准确处理处罚资源的复制,克制浅拷贝导致的题目。
  
  重载运算符:
        通过重载运算符,可以为自界说类赋予与内置范例雷同的运算举动,使代码更具可读性和天然性。重载时须要依照肯定的规则,如参数数目与对应内置运算符的运算对象数目同等,不能创建新的运算符等。同时,要根据类的实际需求和语义来决定哪些运算符须要重载以及怎样重载。
  疑难明答

     1.this指针的用法

     this 指针指向的就是当前正在操纵的对象的地点。通过 this 指针,成员函数可以访问和操纵当前对象的成员变量和其他成员函数。
     this 指针是 C++ 类中一个非常紧张且特殊的概念,以下为您更详细地表明其作用和用法:
      作用:
  

  • 办理成员变量和局部变量同名辩论:在类的成员函数内部,假如存在与成员变量同名的局部变量,使用 this 指针可以明确地指定要操纵的是成员变量。
  • 作为函数返回值:使得成员函数可以或许返回调用它的对象本身,从而支持链式编程风格,让代码更加简便和直观。
  • 在成员函数中通报对象的地点:某些环境下,须要在成员函数内部将当前对象的地点通报给其他函数或举行一些与对象地点相干的操纵,这时可以使用 this 指针。
用法:
       (1)访问成员变量:
        当成员函数内部须要访问类的成员变量时,可以使用 this->成员变量名 的方式来明确指定要操纵的是类中的成员变量,而非同名的局部变量。
        比方:
  1. class MyClass
  2. {
  3.     private:
  4.         int value;
  5.     public:
  6.         void setValue(int value)
  7.         {
  8.             this->value = value;  // 这里使用 this 指针明确是成员变量 value
  9.         }
  10. };
复制代码
       (2)作为返回值:
  1. class Counter
  2. {
  3.     private:
  4.         int count;
  5.     public:
  6.         Counter& increment()
  7.         {
  8.             count++;
  9.             return *this;  // 返回当前对象的引用
  10.         }
  11. };
  12. Counter c;
  13. c.increment().increment();  // 链式调用,count每次+1
复制代码
       (3)通报对象地点:
  1. class SomeClass
  2. {
  3.     public:
  4.         void process()
  5.     {
  6.             anotherFunction(this);  // 将当前对象的地址传递给另一个函数
  7.     }
  8. };
复制代码
        值得一提的是,C++规定不能在实参和形参的位置表现的写this指针(编译时编译器会处理处罚),但是可以在函数体内表现使用this指针。
    2.为什么拷贝构造函数的第一个参数必须是类范例对象的引用?

        当我们须要创建一个新的对象,并使用现有的对象来初始化这个新对象时,就会调用拷贝构造函数。我们先来看个例子:
  1. //拷贝构造函数
  2. Date (Date d)
  3. {
  4.         _year=d._year;
  5.         _month=d._month;
  6.         _day=d._day;
  7. }
  8. int main()
  9. {
  10.         Date d1(2030,1,1);
  11.         Date d2(d1);
  12.         d2.Print();
  13. }
复制代码
        在main函数中实验Date d2(d1);时,就会出现无穷递归的环境。
        这是由于在拷贝构造函数的参数通报过程中,默认是按值通报。也就是说,当您通报 d 到拷贝构造函数时,实际上会调用拷贝构造函数来创建这个参数 d ,而在创建这个参数 d 的过程中又会调用拷贝构造函数,云云就形成了无穷递归。
        如图:

        着实不明确也没关系,记取本身写拷贝构造函数时,参数为类范例的引用即可。
    3.函数返回引用的注意事项

        传值返回会产生一个临时对象调用拷贝构造,传值引用返回,返回的是返回对象的别名(引用),没有产生拷贝。但是假如返回对象是一个当前函数局部域的局部对象,函数竣事就烧毁了,那么使用引用返回是有题目的,这时的引用相当于一个野引用,雷同一个野指针一样。传引用返回可以淘汰拷贝,但是肯定要确保返回对象在当前函数竣过后还存在,才华用引用返回。
        这个明确起来不难,下面我给你举个例子你就懂了。
           特殊注意!!!
          给出的这三个函数只是寻常的函数,不是拷贝构造函数!
          当使用拷贝构造函数创建新对象时,体系会自动在内部处理处罚对象的创建和成员的复制,不须要通过返回值来获取效果。 以是在界说拷贝构造函数时,不须要也不能指定返回值。
        它们分别以传值和传引用的方式返回一个 Number 范例的对象。只是通过这两个函数的示例,来分析传值返回和传引用返回在对象复制方面的差别举动和大概出现的题目。
        假设我们有一个类 Number ,用于表现数字。
  1. class Number {
  2. public:
  3.     int value;
  4.     Number(int val)
  5.         {
  6.                 value=val;
  7.         }
  8. };
复制代码
        然后我们有两个函数,一个使用传值返回,一个使用传引用返回:
  1. Number createNumber() {
  2.     Number num(798);
  3.     return num;  // 传值返回,会创建临时对象并调用拷贝构造函数
  4. }
  5. Number& createNumber() {
  6.     Number num(798);
  7.     return num;  // 错误!返回局部对象的引用,函数结束后 num 被销毁,产生野引用
  8. }
复制代码
       一样平常环境下我们直接返回值就行了。不外,当须要克制不须要的对象拷贝以进步性能,大概须要在多个函数调用之间共享和修改同一个对象时,返回引用也是有用的,那就是让这个局部变量变为静态对象,代码如下:
  1. Number& CreatNumber() {
  2.     static Number num(798);  // 使用静态对象,函数结束后不会销毁
  3.     return num;
  4. }
复制代码
4.关于‘++’‘--’运算符的前置后置重载

        在 C++ 中,当重载 ++ 运算符时,存在前置 ++ 和后置 ++ 两种环境。
        前置 ++ 表现先增长对象的值,然后返回增长后的值。比方,假如有一个对象 obj ,实验 ++obj 就是前置 ++ 操纵。后置 ++ 则是先返回对象当前的值,然后再增长对象的值。比如,对同一个对象 obj 实验 obj++ 就是后置 ++ 操纵。
        由于这两种操纵的逻辑差别,以是须要通过差别的重载函数来实现。但题目是,它们的函数名都只能是 operator++ ,这就导致无法直接通过函数名来区分。为相识决这个题目,C++ 规定,后置 ++ 重载时,增长一个 int 范例的形参。这个形参实际上不会被使用,只是用于和前置 ++ 重载函数举行区分。
        模板如下:
  1. class MyClass {
  2. public:
  3.     MyClass& operator++() {  // 前置 ++ 重载
  4.         // 增加对象内部状态的相关操作
  5.         return *this;
  6.     }
  7.     MyClass operator++(int) {  // 后置 ++ 重载,通过 int 形参与前置 ++ 区分
  8.         MyClass temp = *this;
  9.         // 增加对象内部状态的相关操作
  10.         return temp;
  11.     }
  12. };
复制代码
        学会了?那我们来实现一个对于日期类的++吧,下面是作者的代码。什么?为什么没有写后置++?固然是留给你练手了!差异不大,注意一下参数和返回值就行。
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int daymaxp[13]={-1,31,28,31,30,31,30,31,31,30,31,30,31};//平年每个月的天数
  4. int daymaxr[13]={-1,31,28,31,30,31,30,31,31,30,31,30,31};//闰年每个月的天数
  5. class Date
  6. {
  7.         public:
  8.                 Date(int a,int b,int c)//构造函数
  9.                 {                               
  10.                         _year=a;
  11.                         _month=b;
  12.                         _day=c;
  13.                 }
  14.                 Date& operator++()//前置++重载
  15.                 {
  16.                         _day+=1;
  17.                         if((_year%4==0&&_year%100!=0)||(_year%400==0))//是闰年
  18.                         {
  19.                                 if(_day>daymaxr[_month])//超出当前月份最大天数
  20.                                 {
  21.                                         _month+=1;
  22.                                         _day=1;
  23.                                         if(_month>12)//超过12月
  24.                                         {
  25.                                                 _year+=1;//年份加一
  26.                                                 _month=1;//月份变为1月
  27.                                         }
  28.                                 }
  29.                         }
  30.                        
  31.                         else
  32.                         {
  33.                                 if(_day>daymaxp[_month])
  34.                                 {
  35.                                         _month+=1;
  36.                                         if(_month>12)
  37.                                         {
  38.                                                 _year+=1;
  39.                                                 _month=1;
  40.                                         }
  41.                                 }
  42.                         }
  43.                         return *this;
  44.                 }
  45.                 void print()//打印函数
  46.                 {
  47.                         cout<<_year<<"年"<<_month<<"月"<<_day<<"日"<<endl;
  48.                 }               
  49.         private:
  50.                 int _year;
  51.                 int _month;
  52.                 int _day;
  53. };
  54. main()
  55. {
  56.         Date today(2024,12,31);
  57.         Date tomorrow=++today;
  58.         tomorrow.print();//打印明天
  59.         today.print();//由于是前置++,此时today也变大了一天
  60. }
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表