科技公司网站响应式,商城系统哪家的好,长春建个网站需要多少钱?,网站建设相关博客#x1f388;归属专栏#xff1a;浅尝C #x1f697;个人主页#xff1a;Jammingpro #x1f41f;记录一句#xff1a;好想摆烂#xff0c;又好想学习~~ 文章前言#xff1a;本篇文章简要介绍C类的构造函数、析构函数及拷贝构造函数#xff0c;介绍每个小点时#xf… 归属专栏浅尝C 个人主页Jammingpro 记录一句好想摆烂又好想学习~~ 文章前言本篇文章简要介绍C类的构造函数、析构函数及拷贝构造函数介绍每个小点时都会附上对应的代码如果可能的话。余下3大默认成员函数将于后续文章中叙述要不整个文章太长了。 文章目录 构造函数基本概念特性 析构函数基本概念特性 拷贝构造函数概念特征 我们知道如果一个类什么都没有那么它就是一个空类。空类真的什么都没有吗答案是否定的。任何类在什么都不写时编译器会自动生成以下6个默认成员函数本文将先介绍构造函数、析构函数及拷贝构造函数。 ps默认成员函数用户没有显式实现编译器会生成的成员函数称为默认成员函数。
构造函数
基本概念
在介绍构造函数之前我们先回顾一下类的定义和调用。
#includeiostream
using namespace std;class Date
{
public:void Init(int year, int month, int day){m_year year;m_month month;m_day day;}void Print(){cout m_year - m_month - m_day endl;}
private:int m_year;int m_month;int m_day;
};int main()
{Date date;date.Init(2023, 1, 1);date.Print();return 0;
}对于Date类可以通过 Init 公有方法给对象设置日期但如果每次创建对象时都调用该方法设置信息未免有点麻烦那能否在对象创建时就将信息设置进去呢 构造函数是一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用以保证每个数据成员都有 一个合适的初始值并且在对象整个生命周期内只调用一次。
特性
构造函数是特殊的成员函数需要注意的是构造函数虽然名称叫构造但是构造函数的主要任务并不是开空间创建对象而是初始化对象。 特征如下 ①函数名与类名相同。 ②无返回值。 ③对象实例化时编译器自动调用对应的构造函数。 ④构造函数可以重载。
#includeiostream
using namespace std;class Date
{
public://以下两个构造函数参数个数不同构成重载Date(int year, int month, int day){//定义对象时会自动打印下面这句内容cout Date(int year, int month, int day)被调用 endl;m_year year;m_month month;m_day day;}//构造函数名称与类名相同且无返回值Date(int year){cout Date(int year)被调用 endl;}
private:int m_year;int m_month;int m_day;
};int main()
{Date d1(2022, 12, 12);Date d3(2023);return 0;
}⑤如果类中没有显式定义构造函数则C编译器会自动生成一个无参的默认构造函数一旦用户显式定义编译器将不再生成。
#includeiostream
using namespace std;class Date
{
public:/*提供该构造函数后编译不再生成默认构造函数Date(int year, int month, int day){m_year year;m_month month;m_day day;}*/
private:int m_year;int m_month;int m_day;
};int main()
{Date d1;//若将上面类中定义的构造函数注释掉则此处不会出错因为编译器自动生成了默认构造函数不带参数的函数//若使用上面类中定义的构造函数则此处定义d1对象会报错因为编译器不再自动生成默认构造函数return 0;
}⑥关于编译器生成的默认成员函数不实现构造函数的情况下编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用d对象调用了编译器生成的默认构造函数但是d1对象m_year/m_month/m_day依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用 答C把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型如int/char…自定义类型就是我们使用class/struct/union等自己定义的类型看看下面的程序就会发现编译器生成默认的构造函数会对自定类型成员m_t调用的它的默认成员函数。
#includeiostream
using namespace std;class Time
{
public:Time(){cout Time构造函数 endl;m_hour m_minute m_second 0;}void Print(){cout m_hour : m_minute : m_second endl;}
private;int m_hour;int m_minute;int m_second;
};class Date
{
public:void Print(){cout m_year - m_month - m_day endl;m_t.Print();}
private:int m_year;int m_month;int m_day;
};int main()
{Date d;//d未调用Time的构造函数但打印时Time的各个成员变量均已初始化为1。说明编译器自动实现的Date的构造函数会自动调用Time的构造函数。d.Print();return 0;
}注意C11 中针对内置类型成员不初始化的缺陷又打了补丁即内置类型成员变量在类中声明时可以给默认值。
#includeiostream
#includestring
using namespace std;class Jammingpro
{
public:void Print(){cout 我是 m_name 今年 m_age 岁 endl;}
private:string m_name xioaming;int m_age 18;
};int main()
{Jammingpro jam;jam.Print();//打印的是默认值return 0;
}⑦无参的构造函数和全缺省的构造函数都称为默认构造函数并且默认构造函数只能有一个。 注意无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数都可以认为是默认构造函数。下面代码演示的是无参、全缺省构造函数两个函数只能留下其中一个不能同时存在。
class Date
{
public:Date(){}Date(int year 2022, int month 12, int day 12){m_year year;m_month month;m_day day;}
private:int m _year;int m_month;int m_day;
}析构函数
基本概念
与构造函数功能相反析构函数是完成对对象本身的销毁局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数完成对象中资源的清理工作。
特性
析构函数是特殊的成员函数其特征如下 ①析构函数名是在类名前加上字符 ~。 ②无参数无返回值类型。 ③一个类只能有一个析构函数。若未显式定义系统会自动生成默认的析构函数。注意析构函数不能重载 ④对象生命周期结束时C编译系统系统自动调用析构函数。
#includeiostream
using namespace std;class Stack
{
public://对象生命周期结束时需要调用下面的析构函数~Stack(){cout ~Stack() endl;}
private:char* m_mem;int m_size;int m_capacity;
};int main()
{Stack stk;return 0;
}⑤关于编译器自动生成的析构函数是否会完成一些事情呢下面的程序我们会看到编译器生成的默认析构函数对自定类型成员调用它的析构函数。
#includeiostream
using namespace std;class Time
{
public://下面这句话会被打印因为Date的自动生成的析构函数会调用自定义数据类型的析构函数~Time(){cout Time析构函数 endl;}
private;int m_hour;int m_minute;int m_second;
};class Date
{
private:int m_year;int m_month;int m_day;
};int main()
{Date d;return 0;
}⑥如果类中没有申请资源时析构函数可以不写直接使用编译器生成的默认析构函数比如Date类有资源申请时一定要写否则会造成资源泄漏比如Stack类。
class Stack
{
public:Stack(){m_mem new char[100];//在堆区申请了内存空间m_size 0;m_capcpity;}~Stack(){delete[] m_mem;//必须使用析构函数释放资源}
private:char* m_mem;int m_size;int m_capacity;
}拷贝构造函数
概念
拷贝构造函数只有单个形参该形参是对本类类型对象的引用(一般常用const修饰)在用已存在的类类型对象创建新对象时由编译器自动调用。
特征
其特征如下 ①拷贝构造函数是构造函数的一个重载形式。 ②拷贝构造函数的参数只有一个且必须是类类型对象的引用使用传值方式编译器直接报错因为会引发无穷递归调用。
class Date
{
public:Date(int year, int month, int day){m_year year;m_month month;m_day day;}//Date(const Date d) -错误写法将引发无穷递归Date(const Date d)//-正确写法{m_year d.m_year;m_month d.m_month;m_day d.m_day;}
private:int m_year;int m_month;int m_day;
}③若未显式定义编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝这种拷贝叫做浅拷贝或者值拷贝。 注意在编译器生成的默认拷贝构造函数中内置类型是按照字节方式直接拷贝的而自定义类型是调用其拷贝构造函数完成拷贝的。
class Stack
{
public:Stack(){m_mem new char[100];m_size 0;m_capacity 100;}/*下面这个是编译器自动生成的Stack(const Stack s){m_mem s.m_mem;m_size s.m_size;m_capacity s.m_capacity;}*/Stack(const Stack s){m_mem new char[s.m_capacity];strcpy(m_mem, s.m_mem);m_size s.m_size;m_capacity s.m_capacity;}
private:char* m_mem;int m_size;int m_capacity;
}④编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了还需要自己显式实现吗当然像Date类这样的类是没必要的。而像上面的Stack类是需要自己显示实现拷贝构造函数的。 编译器自动生成的拷贝构造函数是浅拷贝也就是将属性值复制一份。对于下面图片中的s1和s2由于s2复制了s1中m_mem的地址导致两个栈指向同一内存空间两个栈生命周期结束后均会调用析构函数此时将执行两次delete[] m_mem将导致内存重复释放而报错。 注意类中如果没有涉及资源申请时拷贝构造函数是否写都可以一旦涉及到资源申请时则拷贝构造函数是一定要写的否则就是浅拷贝。 ⑤拷贝构造函数典型调用场景 使用已存在对象创建新对象 函数参数类型为类类型对象 函数返回值类型为类类型对象
#includeiostream
using namespace std;class Date
{
public:Date(int year, int month, int day){cout Date create endl;m_year year;m_month month;m_day day;}Date(const Date d){cout Date copy endl;m_year d.m_year;m_month d.m_month;m_day d.m_day;}~Date(){cout ~Date() endl;}
private:int m_year;int m_month;int m_day;
};Date Test(Date d)
{return d;
}int main()
{Date date(2023, 11, 11);Test(date);Date cp(date);return 0;
}这段代码的执行结果如下即调用1次构造函数3次拷贝构造函数4次析构函数
Date create
Date copy
Date copy
~Date()
~Date()
Date copy
~Date()
~Date()对于上面的代码执行Date date(2023, 11, 11);时调用1次构造函数执行Test(date);时传递参数调用了1次拷贝构造函数返回时返回值又调用了1次拷贝构造函数由于返回值产生的临时变量和Test的参数d离开Test函数后生命周期就结束了因此分别调用了析构函数即2次析构函数执行Date cp(date)构造cp对象时调用了1次拷贝构造函数main函数执行结束date与cp生命周期结束分别调用析构函数即2次析构函数。 为了提高程序效率一般对象传参时尽量使用引用类型返回时根据实际场景能用引用尽量使用引用。 文章结语这篇文章对C中的构造函数、析构函数及拷贝构造函数进行了简要的介绍。后续文章将介绍余下3大默认成员函数。 欢迎进入浅尝C专栏查看更多文章。 如果上述内容有任何问题欢迎在下方留言区指正b(▽)d