C++ 】类的6个默认成员函数1( 构造、析构、拷贝构造函数)

类的6个默认成员函数

之前说,如果一个类中的什么成员都没有,就称为空类。其实,并不是这样的,任何一个类在我们什么都不写的情况下,都会自动生成6个默认成员函数。
在这里插入图片描述

1.构造函数

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只调用一次。

构造函数是一个特殊的成员函数,因为,构造函数的名称虽然是构造,但要注意构造函数的主要任务并不是开空间创建对象,而是初始化对象。(可以在创建对象时直接初始化)
特征:
1.函数名与类名相同
2.无返回值
3.对象实例化时编译器自动调用对应的构造函数
4.构造函数可以重载。
在这里插入图片描述
5.如果类中没有显示定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显示定义,编译器则不再生成。
6.无参构造函数和全缺省的构造函数都称为构造函数,并且默认构造函数只能有一个。注:无参构造函数、全缺省构造函数,没写编译器默认生成的构造函数,都可以认为默认成员函数

class Date
{
public:
 //构造函数----完成初始化工作
 Date()//无参构造函数
 {
    _year = 1999;
    _month = 1;
    _day = 23;
 }
 Date(int year=1922, int month=2, int day=18)//有参构造函数 
 {
    _year = year;
    _month = month;
    _day = day;
 }
private:
 int _year;
 int _month;
 int _day; 
};
void Test()
{ 
   Date d;//构造函数的调用不明确
}

在这里插入图片描述
7.成员变量的命名风格。
测试代码1
.h文件

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
class Date
{
public:
 //构造函数----完成初始化工作
 Date();//无参构造函数
 Date(int year, int month, int day);//有参构造函数 
void showDate();//显示
 //void InitDate(int year, int month, int day);//初始化
private:
 int _year;
 int _month;
 int _day;
};

.cpp文件

#include "Date.h"
Date::Date()//无参构造函数
{
 _year = 1203;
 _month = 12;
 _day = 7;
 }
//构造函数   完成初始化工作
Date::Date(int year, int month, int day)//构造函数   完成初始化工作
{
_year = year;
 _month = month;
 _day = day;
}
//void Date::InitDate(int year, int month, int day)
//{
// _year = year;
// _month = month;
// _day = day;
//}
void Date::showDate()
{
 cout << _year << "年" << _month << "月" << _day <<"日" <<endl;
 int main()
{
 Date s1;//调用无参构造函数
 Date s2(2019, 12, 6);//调用有参构造函数
 //注意:如果通过无参构造函数创建对象时,对象后面不用括号,否则就成了函数声明
 //
 //Date s3();//函数声明
  /*Date s1;
 s1.InitDate(2019, 12, 6);*/
 s1.showDate();
 s2.showDate();
 system("pause");
 return 0;
}

2.析构函数

通过构造函数,知道了对象是怎么来的,析构函数带你了解对象是怎么没的
析构函数,是特殊的成员函数,与构造函数功能相反,不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作
特征:
1.析构函数名是在类名前加上字符~
2.无参数无返回值(销毁了就没有返回值,有一个this参数,编译器自己维护的,用户加不了参数)(没有参数自然也就不能重载)
3.一个类只有一个析构函数。若是未显示定义,系统会自动生成默认的析构函数。(如果这个类涉及资源,一定要自己写析构函数,并且在里面把资源清理掉,编译器生成的析构函数没有用,不做什么事情)
4.对象生命周期结束时,C++编译系统自动调用析构函数。
代码2理解下:

#include<cstdio>
#include<cstdlib>
#include<cassert>
#include<iostream>

using namespace std;

class SeqList
{
public:
 SeqList(int capacity = 10)//构造函数
 {
  cout << "SeqList(int):" << this << endl;//测试
  _array = (int*)malloc(capacity*sizeof(int));
  assert(_array);
  _capacity = capacity;
  _size = 0;
 }
 ~SeqList()//析构函数
 {
  if (_array)//资源存在就开始清理
  {
   free(_array);//释放堆上的空间
   _array = NULL;//将指针设置为空
   _capacity = 0;//空间释放了,容量和大小都置为空
   _size = 0;
  }
  cout << "~SeqList():" << this << endl;//测试
   }
private:
 int* _array;
 int _capacity;
 int _size;
};
void TestSeqList()
{
 SeqList s;创建一个对象(实例化一个对象)
}
int main()
{
 TestSeqList();
 system("pause");
 return 0;
 }

结果也可证明构造函数、析构函数在对象的生命周期内只调用一次
在这里插入图片描述
5.关于编译器自动生成的析构函数,是否会完成一些事情呢?举个例子,会看到,编译器生成的默认析构函数,对自定义成员调用它的析构函数
在这里插入图片描述
代码3

class String//封装字符串
{
public:
 String(const char* str="")
 {
  cout << "String(const char* ):" << this << endl;
  _str = (char*)malloc(strlen(str) + 1);//开辟空间
  strcpy(_str, str);
 }
 ~String()
 {
  if (_str == nullptr)
  {
   _str = "";
  }
  free(_str);
  _str = nullptr;
  cout << "~String():" << this << endl;
   }
private:
 char* _str;
};
class Person//写这个类的目的是,测试编译器会不会合成默认的析构函数
{
//生成:默认构造函数,将对象中的_name和_gender两个String类型的对象构造好
private:
 String _name;
 String _gender;
 int _age;
//所以,创建两个String类的对象,就要调用String类的构造函数
};
void TestPerson()
{
 Person s;//创建对象 
}
//出了作用域,要销毁函数,Person里没有,
//生成:默认析构函数,将对象中的_name和_gender两个String类型的对象销毁
//销毁 s对象,要把包含String类的对象销毁,所以要调String类的析构函数
//以上都是编译器自动完成的
int main()
{
 TestPerson();
 system("pause");
 return 0;
}

3.拷贝构造函数

如果我们想在创建对象时,创建一个与一个对象一模一样的新对象,就要用到拷贝构造了,
拷贝构造函数:它也是特殊的成员函数,只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
特征:
1.拷贝构造函数是构造函数的一个重载形式。
2.拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。(两个参数虽然可以编译过,但就不叫拷贝构造函数了)
在这里插入图片描述
代入法:Date d2(d1);
赋值法:Date d2=d1;

3.若未显示定义,系统生成的默认拷贝构造函数。默认的拷贝构造对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝(共用同一块内存空间(把栈上的内容拷贝走,没有管堆上的,导致指针指向的内容是同一份堆上的内容))

PS:有时候,编译器生成的默认构造函数有问题,没有资源就不需要去释放(比如,像代码1:日期类) ,就默认生成拷贝构造,类里面管理资源就一定要自己写拷贝构造//必须显示提供自己的拷贝构造函数,否则同一份资源会被释放多次,指针成为野指针(比代码3

在这里插入图片描述
代码4

class Date
{
public:
 //构造函数
 Date(int year = 2019, int month = 5, int day = 14)
 {
  cout << "Date(int,int,int):" << this << endl;
  _year = year;
  _month = month;
  _day = day;
 }
 //拷贝构造函数
 Date(const Date& d)
 {
  cout << "Date(const Date&)"<< this << endl;
  _year = d._year;
  _month = d._month;
  _day = d._day;
 }
 ~Date()//析构函数
 {
  cout << "~Date()" << this << endl;
 }
private:
 int _year;
 int _month;
 int _day;
};
void TestDate()
{
 Date d1(2019, 3, 29);
 Date d2(d1);
 Date d3(2018, 3, 29);
// d3 = d1;////假如日期错了,可以赋值,将对象d1的值赋给对象d3
//但有资源这样做就会导致内存泄漏
//还是不用为好
 }
发布了73 篇原创文章 · 获赞 2 · 访问量 2862

猜你喜欢

转载自blog.csdn.net/weixin_43219708/article/details/104108768