做网站要花钱吗/赣州seo培训

做网站要花钱吗,赣州seo培训,哪些网站可以做公司制度,在电脑上建设网站目录 前言 1. 类的6个默认成员函数 2. 构造函数 2.1 概念 2.2 特性 3. 析构函数 3.1 概念 3.2 特性 4. 拷贝构造函数 4.1 概念 4.2 特征 总结 前言 本文主要讲解类中构造函数、析构函数和拷贝构造函数。关于这三个类中默认成员函数的知识点很多,有许多…

目录

前言

1. 类的6个默认成员函数

2. 构造函数

2.1 概念

2.2 特性

3. 析构函数

3.1 概念

3.2  特性

4. 拷贝构造函数

4.1 概念

4.2 特征

总结


前言

本文主要讲解类中构造函数、析构函数和拷贝构造函数。关于这三个类中默认成员函数的知识点很多,有许多代码示例,干货满满!


1. 类的6个默认成员函数

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

  • 构造函数:完成初始化的工作
  • 析构函数:完成清理资源的工作
  • 拷贝构造:使用同类型对象初始化创建对象
  • 赋值重载:把一个对象赋值给另外一个对象
  • 取地址重载:主要是普通对象和const对象取地址

 

2. 构造函数

2.1 概念

对于以下Date类:

class Date
{
public:
void Init(int year, int month, int day)
{_year = year;_month = month;_day = day;
}void Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Init(2024, 4, 12);d1.Print();Date d2;d2.Init(2024, 4, 13);d2.Print();return 0;
}

 对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?

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

 

2.2 特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。其特征如下:

  • 函数名与类名相同
  • 无返回值。
  • 对象实例化时编译器自动调用对应的构造函数。
  • 构造函数可以重载。
class Date
{
public:Date(){}Date(int year = 1, int month = 1,int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2(2024, 4, 2);// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象Date d3(); //无法跟函数声明区分开return 0;
}

 

  • 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
class Date
{
public:/*// 如果用户显式定义了构造函数,编译器将不再生成Date(int year, int month, int day){_year = year;_month = month;_day = day;}*/void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{// 将Date类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数// 将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成// 无参构造函数,放开后报错Date d1;return 0;
}

 

  • 在不实现构造函数的情况下,编译器会生成默认的构造函数。但是Date类型对象调用了编译器生成了默认构造函数,对成员变量_year,_month和_day初始化之后还是随机值,那么编译器自动生成的构造函数有什么用呢?
  • C++把类型分成内置类型(基本类型)自定义类型。内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数。
class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};class Date
{
private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t;
};int main()
{Date d;return 0;
}

 

  • 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
    注意:无参构造函数全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。(就是不用传递参数的函数
class Date
{
public:Date(){_year = 1900;_month = 1;_day = 1;}Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
// 以下测试函数能通过编译吗?
void Test()
{Date d1;
}int main()
{Test();return 0;
}

上面的代码无法正常运行,因为全缺省构造函数和无参构造函数都不需要传递参数,这样子编译器无法判断要调用哪个构造函数,会发生歧义。我们一般使用保留全缺省构造函数,因为它既可以当做无参构造函数,也可以当做有参构造函数使用。

 

3. 析构函数

3.1 概念

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

注意:析构函数不是销毁对象,而是做清理资源的工作!

 

3.2  特性

析构函数是特殊的成员函数,其特征如下:

  •  析构函数名是在类名前加上字符 ~。
  •  无参数无返回值类型
  •  一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  •  对象生命周期结束时,C++编译系统系统自动调用析构函数。
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){if (_array){cout << "~Stack()" << endl;free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:DataType* _array;int _capacity;int _size;
};void TestStack()
{Stack s;s.Push(1);s.Push(2);
}

我们可以再析构函数里面加一个打印,这样可以知道程序结束后编译器是否有自动调用析构函数。

ecc9817933714994b3da32ede818d003.png

 

  • 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数。
  • 注意:内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型。

我在VS2022上运行这段代码,观察编译器自带的析构函数会做些什么

class Time
{
public:~Time(){cout << "~Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};class Date
{
private:// 基本类型(内置类型)int _year = 1970;int _month = 1;int _day = 1;// 自定义类型Time _t;
};int main()
{Date d;return 0;
}

默认生成的析构函数,的确对自定义类型成员调用了它的析构函数。

76b0c62e4cd24aebb15baf3ccddadceb.png

我们进入监视窗口查看各个函数值的变化,当程序走到结束的时候,对象d中的年月日没有发生任何变化,自定义类型成员变量_t的小时、分钟和秒,发生了改变,变成析构函数中的赋值。

0e2657257f4f46edaf812346659b5fa9.png

总结:

  • 内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;
  • 而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。
  • 所以一般类只含有不申请空间的内置类型成员,或者不仅含有不申请空间的内置类型成员和自定义类型,都不需要自己写析构函数。
  • 只有那种类中包含需要申请空间的内置类型成员,才需要写析构函数,清理申请的空间。

注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数

 

4. 拷贝构造函数

4.1 概念

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

 

4.2 特征

拷贝构造函数也是特殊的成员函数,其特征如下:

  • 拷贝构造函数是构造函数的一个重载形式。
  • 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// Date(const Date& d)   // 正确写法Date(const Date d) //错误写法,编译报错{_year = d._year;_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2(d1);return 0;
}

 

那为什么传类类型参数就会引发无穷递归?我们来看下面的代码,我们调用fun函数,传递的参数是A类的对象a,我们将A类的拷贝构造函数中加入打印“A(A& a)”,表明调用了拷贝构造函数。

class A
{
public:A(){}A(A& a){cout << "A(A& a)" << endl;}
};void fun(A _a)
{}int main()
{A a;fun(a);return 0;
}

 

运行结果如下,fun函数接受类类型对象时调用了拷贝构造。因为形参是实参的一份临时拷贝。内置类型的数据传参直接开辟一块空间进行值拷贝。自定义类型传参时,需要调用该类型的拷贝构造函数创建一个形参。

b7486191f514489bad6cdd4157947bb3.png

  • 所以回到最开始的问题,为什么会引发无穷递归呢?因为d2是调用拷贝构造函数进行初始化,需要传递参数,参数是d1,而自定义类型的参数创建形参时,需要调用该类型的拷贝构造函数。
  • 会发生初始化d2之前先要拷贝d1这个参数,此时形参调用拷贝构造函数也需要传参。参数是d1,会再次调用拷贝构造函数。这样就会造成无穷递归的情况。

1446573e87dc4100a3ed723c8db6286e.png

 

  • 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝
class Time
{
public:Time(){_hour = 1;_minute = 1;_second = 1;}Time(const Time& t){_hour = t._hour;_minute = t._minute;_second = t._second;cout << "Time::Time(const Time&)" << endl;}
private:int _hour;int _minute;int _second;
};class Date
{
public:Date(int year = 1970, int month = 1, int day = 1){_year = year;_month = month;_day = day;}private:// 基本类型(内置类型)int _year = 1970;int _month = 1;int _day = 1;// 自定义类型Time _t;
};int main()
{Date d1;// 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数// 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构造函数Date d2(d1);return 0;
}

运行结果如下,调用了自定义类型成员的拷贝构造函数。

7fc4997a2df049c39544a50448aab557.png

观察调试时的监视窗口,会发现编译器自动生成的默认拷贝构造函数,对内置类型成员进行了值拷贝,对自定义类型成员调用了它的拷贝构造函数。

9ac251868e294578810f1f724e5bce46.png

 

  • 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?当然像日期类这样的类是没必要的。那下面的类呢,运行一下下面的代码看看。
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2(s1);return 0;
}

运行结果如下,直接进行了报错,这是为什么呢?

fb5bbeea6e104381b3ade970f803c3b3.png

  • 因为默认生成的拷贝构造函数进行的是按字节拷贝,也就是值拷贝。然而Stack类里面的_array成员是一个整形指针,指向一块动态开辟的空间,默认拷贝构造函数会将s1里的_array成员存储的地址,按字节拷贝到s2的_array成员中,导致这两个指针指向同一块空间。
  • 由于它们指向同一块空间的时候,当程序结束时,对这两个类对象进行销毁,会调用析构函数,会对同一块空间进行了两次释放操作,这就是报错的原因。

4a77e3f7a68449a6a774247a88d2381b.png

 

拷贝构造函数典型调用场景:

  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象

我们尝试运行下面这段代码:

class Date
{
public:Date(int year, int minute, int day){cout << "Date(int,int,int):" << this << endl;}Date(const Date& d){cout << "Date(const Date& d):" << this << endl;}~Date(){cout << "~Date():" << this << endl;}
private:int _year;int _month;int _day;
};Date Test(Date d)
{Date temp(d);return temp;
}int main()
{Date d1(2024, 4, 16);Test(d1);return 0;
}

运行结果如下,一开始初始化d1调用了构造函数,调用Test函数,传递自定义类型的参数,调用拷贝构造函数来拷贝出个形参,temp也进行拷贝,再次调用拷贝构造函数。

f0b869f3688d441ebf38c912579c6417.png

 

 


总结

这篇文章对三个默认成员函数进行了详细的解析,看完的小伙伴不要忘记亲自上手写代码,进行练习,多多重复,百炼成钢!

创作不易,希望这篇文章能给你带来启发和帮助,如果喜欢这篇文章,请留下你的三连,你的支持的我最大的动力!!!

ee192b61bd234c87be9d198fb540140e.png

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/100.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视觉SLAM学习打卡【10】-后端·滑动窗口法位姿图

本节是对上一节BA的进一步简化&#xff0c;旨在提高优化实时性.难点在于位姿图部分的雅可比矩阵求解&#xff08;涉及李代数扰动模型求导&#xff09;&#xff0c;书中的相关推导存在跳步&#xff08;可能数学功底强的人认为过渡的理所当然&#xff09;&#xff0c;笔者参考了知…

【Gradle】Gradle的构建过程

Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置&#xff0c;也增加了基于Kotlin语言的kotlin-based DSL&#xff0c;抛弃了基于XML的各种繁琐配置。 面向Java应用为主。当前其支持的语言C、J…

免费网址导航网站源码v1.7.0最新版内置多个模板

效果图 PC端&移动端&#xff08;共有7款主题&#xff09; 1.5IUX搜索 2.Onenav主题 六零二开 3.基于彩虹工具网修改 4.默认主题 5.基于default主题开发 6.极简个人主页主题 7.孤客主题 后台管理效果图 项目说明 孤客导航页&#xff08;LoSFeR&#xff09;是一个…

Zephyr Windows开发环境搭建

Zephyr 如果有错误或未及时更新&#xff0c;请以官网文档为主 官网&#xff1a;https://docs.zephyrproject.org/latest/develop/getting_started/index.htm 下载安装 Chocolatey 这是一个类似于在Linux系统下 yum 和 apt 那样的包管理器 官网&#xff1a;https://chocolat…

安宝特方案 | AR工业解决方案系列-工厂督查

在工业4.0时代&#xff0c;增强现实&#xff08;AR&#xff09;技术正全面重塑传统工业生产&#xff0c;在工厂监督领域&#xff0c;其应用不仅大幅提升了生产效率、监测准确性和规范执行程度&#xff0c;而且为整体生产力带来了质的飞跃。 01 传统挑战与痛点 在制造业生产流程…

未来趋势:探索Facebook在数字化时代的发展方向

在当今日益数字化的时代&#xff0c;社交媒体已经成为人们日常生活中不可或缺的一部分。Facebook&#xff0c;作为全球最大的社交媒体平台&#xff0c;一直处于行业的前沿&#xff0c;不断地探索和引领社交媒体的发展趋势。本文将深入探讨Facebook在数字化时代的发展方向&#…

如何解决Charles抓包乱码?

一、问题&#xff1a;抓包乱码 二、 1、打开Charles安装的目录下&#xff0c;Charles.ini文件&#xff0c; 2、添加内容、保存 vmarg.5-Dfile.encodingUTF-8 三、重启&#xff0c;再次抓包

Object.assign()用法及详细分析到底是浅拷贝还是深拷贝?

Object.assign方法用于对象的合并&#xff0c;将源对象&#xff08;source &#xff09;的所有可枚举属性&#xff0c;复制到目标对象&#xff08;target&#xff09;。 Object.assign(target,source1,source2) Object.assign方法的第一个参数是目标对象&#xff0c;后面的参数…

Next.js动态路由如何使用

官方解释 Next.js 允许你创建具有 动态路由 的页面。例如&#xff0c;你可以创建一个名为 pages/posts/[id].js 的文件用以展示以 id 标识的单篇博客文章。当你访问 posts/1 路径时将展示 id: 1 的博客文章。 代码 这里先用一个写好的实例代码来展示 import React from react…

Python-Qt上位机设计

1.下载designer软件 2.自己设计一个界面 3.在指定部件加入点击响应命令函数名 鼠标点击目标部件拖出信号线 4.保存生成.ui文件&#xff0c;用pycharm打开 5.生成.py文件 6.新建一个功能文件 上图中class MainWindow的具体代码不予展示。 7.生成exe文件 将写好的py文件保存&a…

java的JDK动态代理

JDK动态代理是指&#xff1a;代理类实例在程序运行时&#xff0c;由JVM根据反射机制动态的生成。也就是说代理类不是用户自己定义的&#xff0c;而是由JVM生成的。 由于其原理是通过Java反射机制实现的&#xff0c;所以在学习前&#xff0c;要对反射机制有一定的了解。传送门&…

【简单介绍下PostCSS】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

python爬虫 - 爬取图片

文章目录 1、爬取图片示例1&#xff1a;使用 .urlretrieve() 函数2、爬取图片示例2 - 使用 open/write 函数3、爬取图片示例33.1 使用 open/write 下载3.2 使用 urlretrieve下载 爬虫的本质&#xff1a;模拟对应的App&#xff0c;浏览器访问对应的地址获取到数据 1、爬取图片示…

57、通过EEG数据的SHAPE变化,揭开EEG-TCNet的黑匣子[看好了小子,我只教这一次]

之前在第18篇博客中对于EEG-TCNet这个处理EEG信号的sota模型进行了介绍&#xff0c;也给出了模型&#xff0c;目前也是全网对于EEG-TCNet浏览度最高的文章了&#xff0c;我觉得讲的已经很细致了&#xff0c;没想到还是有不少同学疑问&#xff0c;这也是全网缺少该模型pytorch代…

3D Gaussian Splatting技术原理

3D Gaussian Splatting 是一种用于体积渲染的技术,特别适用于科学和医学可视化。这种技术使得用户能够以一种直观的方式查看和分析三维数据集,如医学成像数据(MRI、CT扫描)或科学模拟数据。 技术原理 3D Gaussian Splatting基本上是一种将3D空间中的点数据转换成一个连续…

Redis中的Sentinel(五)

Sentinel 检测主观下线状态 在默认情况下&#xff0c;Sentinel会以每秒一次的频率向所有与它创建了命令连接的实例(包括主服务器、从服务器、其他Sentinel在内) 发送PING命令&#xff0c;并通过实例返回的PING命令回复来判断实例是否在线。如图所示&#xff0c;带箭头的连线显…

Word学习笔记之奇偶页的页眉与页码设置

1. 常用格式 在毕业论文中&#xff0c;往往有一下要求&#xff1a; 奇数页右下角显示、偶数页左下角显示奇数页眉为每章标题、偶数页眉为论文标题 2. 问题解决 2.1 前期准备 首先&#xff0c;不论时要求 1、还是要求 2&#xff0c;这里我们都要做一下设置&#xff1a; 鼠…

如何封装Vue组件并上传到npm

前言 环境准备 1.注册npm账号&#xff1a;npm | Home (npmjs.com) 2.保证当前环境安装了vue、webpack、node&#xff0c;以下工作将在该环境下进行&#xff08;没有的小伙伴自行百度安装哈~&#xff09; 3.一下用到的环境版本 webpack&#xff1a;v5.1.4node&#xff1a;v…

SAM5716B 法国追梦DREAM 音频DSP芯片

法国追梦/DERAM SAM5504/5704/5716/5808音频DSP芯片,开发板&#xff0c;方案 可用于电子鼓、电子琴、电吉他、效果器、均衡器、啸叫抑制器等电声产品领域 全系列芯片&#xff1a; SAM2634 SAM2695 SAM5504B SAM5704B SAM5708B SAM5808B SAM5716B SAM5916B... 原厂开发…

根据状态转移图实现时序电路

描述 某同步时序电路的状态转换图如下&#xff0c;→上表示“C/Y”&#xff0c;圆圈内为现态&#xff0c;→指向次态。 请使用D触发器和必要的逻辑门实现此同步时序电路&#xff0c;用Verilog语言描述。 如图所示&#xff1a; 电路的接口如下图所示&#xff0c;C是单bit数据…