C++——类与对象2

打印 上一主题 下一主题

主题 954|帖子 954|积分 2862

类的6个默认成员函数

C++中,当类为空的时候(没有成员),编译器就什么都不做吗?
实在不是的,这时,编译器就会自动生成6个默认成员函数:
   那么,什么是默认成员函数呢?
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数
   6个默认成员函数:
   1.初始化和清算:构造函数,析构函数
  2.拷贝复制:拷贝构造,赋值重载
  3.取地点重载:普通对象和const取地点。
  一:构造函数

   1.构造函数主要是完成初始化的任务。
  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. private:
  11.     int _year;
  12.     int _month;
  13.     int _day;
  14.    
  15. }
  16. int main()
  17. {
  18.     Date d1;
  19.     d1.Init(2021, 3, 9);
  20.     Date d2;
  21.     d2.Init(2024, 7, 6);
  22.     return 0;
  23. }
复制代码
看上面,当我们定义了一个日期的类,我们可以定义一个初始化函数来实现对日期时间的设置,但是呢?这是不是有点麻烦,那么我们可不可以每次调用时之前,它就自动把日期设置初始化好?答案是有的,我们的构造函数就是为了这而生的。
构造函数是一个特殊的成员函数,创建类类型对象时由编译器自动调用,以包管每个数据成员都有 一个符合的初始值,而且在对象整个生命周期内只调用一次
特点:

ps:虽然它叫构造函数,但是它是不会开发空间创建对象的,它的任务只是举行初始化。
1. 函数名与类名相同。
2. 无返回值。
  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.    
  15. }
  16. int main()
  17. {
  18.     Date d1();     //第一种  “Date d1(void)”: 未调用原型函数(是否是有意用变量定义的?)
  19.     Date d2(2022,2,2);   //第二种
  20.     return 0;
  21. }
复制代码
 

说明:
   如果 使用第一种的话,是错误的,声明了d3函数,该函数无参,返回一个日期类型的对象。
它会出现上图的情况,这样调用寻找不到构造函数的,由于编译器会以为你再调用某个函数,所以会识别不到。
  
  1. class Date
  2. {
  3. public:
  4.     Date()
  5.     {
  6.        int _year=2022;
  7.        int _month=2;
  8.        int _day=2;
  9. }
  10.       
  11.    
  12. private:
  13.     int _year;
  14.     int _month;
  15.     int _day;
  16.    
  17. }
  18. int main()
  19. {
  20.     Date d1;   
  21.     return 0;
  22. }
复制代码
  采用无参的时候,对象后面不用跟括号,否则就成了函数声明
   当我们没有写构造函数时,它会出现什么呢?会发现是随机值


   这里我们先来熟悉:
  内置函数/基本类型:语言自己定义的类型eg:int,char,double/指针等等
   自定义类型:用struct/class定义的类型
  
  这里我们默认不写的话编译器就会自动生成,内置函数不做处理(虽然有些编译器会处理,但也只是个性化举动,不是全部的都是),自定义函数会自动去调用默认函数。
  3. 对象实例化时编译器自动调用对应的构造函数。
   4. 构造函数可以重载。
  

  
  5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成 
  1. class Date
  2. {
  3. public:
  4.     /*
  5.     // 如果用户显式定义了构造函数,编译器将不再生成
  6.     Date(int year, int month, int day)
  7.     {
  8.         _year = year;
  9.         _month = month;
  10.         _day = day;
  11.     }
  12.     */
  13.     void Print()
  14.     {
  15.         cout << _year << "-" << _month << "-" << _day << endl;
  16.     }
  17. private:
  18.     int _year;
  19.     int _month;
  20.     int _day;
  21. };
  22. int main()
  23. {
  24.     Date d1;
  25.     return 0;
  26. }
复制代码
  表明:隐藏了构造函数后,那么Date d1还能正常运行吗?
  将Date类中构造函数屏蔽后,代码可以通过编译,由于编译器生成了一个无参的默认构造函

 将Date类中构造函数放开,代码编译失败,由于一旦显式定义任何构造函数,编译器将不再
生成
无参构造函数,放开后报错:error C2512: “Date”: 没有符合的默认构造函数可用 
  在vs2022中, 


   我们可以发现:内置类型函数都要缺省值,且初始化符合我们的要求 
  

   发现:满是自定义类型的构造,且这些类型都默认构造函数。 
    不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??
  C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数

 简单来说:这里我们默认不写的话编译器就会自动生成,内置函数不做处理(虽然有些编译器会处理,但也只是个性化举动,不是全部的都是),自定义函数会自动去调用默认函数。

    7. 无参的构造函数和全缺省的构造函数都称为默认构造函数,而且默认构造函数只能有一个。留意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以以为是默认构造函数。
  1. class Date
  2. {
  3. public:
  4.     Date()   //1个
  5.     {
  6.         _year = 1900;
  7.         _month = 1;
  8.         _day = 1;
  9.     }
  10.     Date(int year = 1900, int month = 1, int day = 1)  //2个
  11.     {
  12.         _year = year;
  13.         _month = month;
  14.         _day = day;
  15.     }
  16. private:
  17.     int _year;
  18.     int _month;
  19.     int _day;
  20. }
复制代码
上面出现了2个构造函数,这是不可以的,只能出现一个。

二:析构函数:

1.什么是析构函数呢?

析构函数:与构造函数功能相反,析构函数不是完成对对象自己的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清算工作。
特性:

   1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,体系会自动生成默认的析构函数。留意:析构函数不能重载
  ps:自动生成默认的析构函数:这里跟构造函数一样:内置函数不做处理,自定义函数会自动去找默认析构函数。
  4. 对象生命周期竣事时,C++编译体系体系自动调用析构函数
  这里以之前写的栈的销毁函数为例:

  1. class Stack
  2. {
  3. public:
  4.        
  5.         Stack(int Decapacity=4)
  6.         {
  7.                 cout << "Stack()" << endl;
  8.                 _a = (int*)malloc(sizeof(int) * Decapacity);
  9.                 if (_a == nullptr)
  10.                 {
  11.                         perror("malloc fail");
  12.                         return;
  13.                 }
  14.                 _top = _capacity = 0;
  15.         }
  16.         ~Stack()
  17.         {
  18.                 cout << "~Stack()" << endl;
  19.                 free(_a);
  20.                 _a = nullptr;
  21.                 _top = _capacity = 0;
  22.         }
  23.         /*
  24.         void Destory()
  25.         {
  26.                 free(_a);
  27.                 _a = nullptr;
  28.                 _top = _capacity = 0;
  29.         }*/
  30. private:
  31.         int* _a;
  32.         int _top;
  33.         int _capacity;
  34. };
  35. int main()
  36. {
  37.         Stack st;
  38.         return 0;
复制代码

上面我们可以检验出来,构造函数和析构函数都是会自动去找的,不用特意去调用。
什么时候须要些析构函数?什么时候又不须要呢?
   1.一样平常情况下,又动态申请的都须要显示写析构函数来释放资源
  2. 没有动态申请的,就不须要写。
  3.须要释放资源的成员都是自定义函数,则不须要写。
  

上面,为什么在main函数中都没有创建Time类的对象,却最后会调用Time的析构函数?
缘故原由: main方法中创建了Date对象d,而d中包罗4个成员变量,其中_year, _month, _day三个是 内置类型成员,销毁时不须要资源清算,最后体系直接将其内存回收即可;而_t是Time类对象,所以在d销毁时,要将其内部包罗的Time类的_t对象销毁,所以要调用Time类的析构函数。
但是: main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要包管其内部每个自定义对象都可以精确销毁,main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数
留意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数

三:拷贝构造函数

什么是拷贝构造函数?

   我们从
可以理解成“复制”的功能,生成逐一个千篇一律的产物。(双胞胎)
  拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一样平常常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
 
  特性:

   1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,由于会引发无穷递归调用。
  1. Date (const Date& d)
  2. {
  3.     _year=d._year;
  4.     _month=d._month;
  5.     _day=d._day;
  6. }
复制代码
若没有以引用,则会出现无限递归的情况。 
 
3.ps:C++中,拷贝构造:内置类型直接拷贝,自定义类型必须要调用拷贝构造。 
4.. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按
字节序完成拷贝(如:内置类型),这种拷贝叫做浅拷贝,大概值拷贝

编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还须要自己显式实现吗?当然像日期类这样的类是没须要的。但是像其他的是须要的:(如栈的拷贝构造)
   1.Date和MyQueue不须要我们写
  3.Stack须要我们写
 
  1. class Stack
  2. {
  3. public:
  4.         Stack(int Decapacity = 4)
  5.         {
  6.                 _a = (int*)malloc(sizeof(int) * Decapacity);
  7.                 if (_a == nullptr)
  8.                 {
  9.                         perror("malloc fail");
  10.                         return;
  11.                 }
  12.                 _size = 0;
  13.                 _capacity = Decapacity;
  14.         }
  15.         int Push(int x)
  16.         {
  17.                 return _a[_size] = x;
  18.                 _size++;
  19.         }
  20.         ~Stack()
  21.         {
  22.                 free(_a);
  23.                 _a = nullptr;
  24.                 _size = _capacity = 0;
  25.         }
  26. private:
  27.         int* _a;
  28.         int _size;
  29.         int _capacity;
  30. };
  31. int main()
  32. {
  33.         Stack st;
  34.         st.Push(1);
  35.         st.Push(2);
  36.         st.Push(3);
  37.         st.Push(4);
  38.         Stack st1(st);
  39.         return 0;
  40. }
复制代码

这里为什么会瓦解呢?
   答:由于这里st调用构造函数创建了4个空间之后,存了1,2,3,4数字。
  当用st去拷贝构造st1时,Stack没有显示拷贝构造函数,所以他会自动去找默认拷贝构造函数,由于它们是内置类型,直接去拷贝的,即将st的值原封不动的拷贝到st1里,这是st和st1就会指向同一个空间,到了竣事的时候,调用到了析构函数,首先要销毁的是st1,已经将st1的空间释放了,但是st并不知道已经释放了,所以它也会再次释放掉,就会重复释放问题,即一个空间多次释放,所以会瓦解。
   
   所以,我们要想成功运行,必须存的时候不是在同一块空间。即:创建一个临时是数组空间。
  1. Stack(Stack& s)
  2. {
  3.         _a = (int*)malloc(sizeof(int) * s._capacity);
  4.         if (_a == nullptr)
  5.         {
  6.                 perror("malloc fail");
  7.                 return;
  8.         }
  9.         memcpy(_a, s._a, sizeof(int) * s._capacity);
  10.         _size = s._size;
  11.         _capacity = s._capacity;
  12. }
复制代码
 好了,本次的分享到此竣事了。
最厥后到我们本次鸡汤部门:

   走自己的路,看自己的风景,活著自己生命的节奏。
  


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

徐锦洪

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表