c++ - 类的默认成员函数
文章目录
- 前言
- 一、构造函数
- 二、析构函数
- 三、拷贝构造函数
- 四、重载赋值操作符
- 五、取地址及const取地址操作符重载
前言
默认成员函数是编译器自动生成的,也可以自己重写,自己重写之后编译器就不再生成,下面是深入了解这些成员函数。
一、构造函数
1、构造函数的特征:
(1). 函数名与类名相同。
(2). 无返回值。
(3). 对象实例化时编译器自动调用对应的构造函数。
(4). 构造函数可以重载。
如:
//构造函数
class test01
{
public://构造函数 - 无参构造函数 无返回值,与类名相同test01(){cout << "test01()" << endl;}//构造函数的重载 - 有参构造 可以重载test01(int a){cout << "test01(int a)" << endl;}private:int _a;};int main()
{test01 p1; //会自动调用无参构造test01 p2(10); //自动调用有参构造return 0;
}
2、需要注意的点
(1)调用无参构造是不要加()
,不然就成函数声明了
如:
//如函数 void Add() void - 类型 Add - 函数名 () - 参数列表//test01 p1(); test01 -类型 p1 - 函数名 () - 参数列表 错误使用test01 p1; //正确使用
(2)无参构造与全缺省重载的构造函数
如:
// 构造函数
class test02
{
public://构造函数 - 无参构造函数 无返回值,与类名相同test02(){cout << "test02()" << endl;}//构造函数的重载 - 有参构造 可以重载test02(int a = 10){cout << "test02(int a)" << endl;}private:int _a;};int main()
{test02 p1;return 0;
}
会出现什么情况捏?
答:出现对重载函数调用的不明确
(3)当我们使用编译器给我生成的默认构造函数时,那么类内的成员变量是否会被初始化呢?
对于内置类型来说:没有初始化出现随机值
对于自定义类型来说:有不需要参数的构造函数就会调用,没有就不会调用。
如:
//结构体
typedef struct N
{
//构造N() { cout << "struct N" << endl; }int i;
}N;//联合体
union E
{E() { cout << "union E" << endl; }int i;
};//类class test02{public:test02(){cout << "test02()" << endl;}private:int _a;};// 构造函数
class test03
{
public:private://内置类型int _a;//自定义类型test02 p;N n;E e;
};int main()
{test03 p1;return 0;
}
3、给成员变量默认值
给成员变量默认值后如果不对其进行赋初值的话,就会使用该默认值。
//构造函数的默认值
class test02
{
public:test02() //不进行任何赋值{//...};test02(int a,int b){//..._a = a;_b = b;}void Print(){cout << _a << endl << _b << endl;}private:
//给变量初始默认值int _a = 10;int _b = 10;};int main()
{test02 a; //使用无参构造test02 b(20, 20); //有参构造a.Print();b.Print();return 0;
}
4、初始化列表
(1)
//这算初始化吗?
test02(int a,int b){_a = a;_b = b;}
答:这不是初始化,因为初始化只能初始化一次,但是在构造函数里可以进行多次的赋值,这只能算是赋初值。
(2)初始化列表格式
在构造函数后面加双引号,在双引号后面加成员变量和括号,括号里是要给成员变量初始化的值。
test03(int a, int b) :_a(a), _b(b)
{//,,,
}
(3)注意
- 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
- 类中包含以下成员,必须放在初始化列表位置进行初始化: 引用成员变量 const成员变量 自定义类型成员(且该类没有默认构造函数时)
- 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
- 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
//初始化列表
class A
{
public:
};class test03
{
public://初始化顺序按声明顺序初始化test03(A a, int b,int c) :_c(c),_a(a),_b(b) {//,,,}private://自定义类型A _a;//const成员const int _b;//引用int& _c;
};
5、作用:
完成初始化工作。
如:
初始化栈
//初始化栈
class Stack
{
public:Stack(int capacity = 4) //利用全缺省参数{_capacity = capacity;_top = 0;//申请空间_a = new int[_capacity];}private://数组指针int* _a;//容量int _capacity;//栈顶后一个位置int _top;
};
二、析构函数
1、析构函数的特征
(1) 函数名 :
~
加上类名。
(2)不能重载。
(3)没有返回值和参数。
(4)在程序结束时自动调用。
如:
class test04
{
public://析构函数 无返回值无参数 不能重载 结束自动调用~test04(){cout << "~test04()" << endl;}
};int main()
{test04 p;return 0;
}
2、需要注意的点
(1)使用编译器自动生成的析构函数在结束时,对于内置类型来说不做处理,因为结束后系统会自动回收,对于自定义类型来说,默认的析构函数会调用成员变量的析构函数进行对该成员变量的清理。
注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数
class test04
{
public:~test04(){cout << "~test04()" << endl;}
};class test05
{
public:private://内置类型int _a;//自定义类型test04 p;
};int main()
{test05 p;return 0;
}
(2)当我们使用编译器生成的析构函数时,该函数对会怎么处理捏?
对内置类型不做处理,对自定义类型会调用其析构函数。
class A
{
public:~A(){cout << "~A()" << endl;}
};class B
{
public://自定义类型A p;//内置类型int a;
};int main()
{B p;return 0;
}
那么申请的资源会被清理吗?
答:是不会的,所以存在需要清理申请的资源时,析构一定要重写。
3、作用
清理申请的资源。
如果没有申请的资源的话不重写用编译器生成的析构函数也行,当我们申请了资源就必须重写析构函数来释放申请的资源了。
如栈的释放:
class Stack
{
public://构造函数Stack(int capacity = 4) //利用全缺省参数{_capacity = capacity;_top = 0;//申请空间 有申请的空间需要在析构函数里释放_a = new int[_capacity];}//析构函数~Stack(){//释放申请的空间free(_a);_a = nullptr;_top = _capacity = 0;}private://数组指针int* _a;//容量int _capacity;//栈顶后一个位置int _top;
};
三、拷贝构造函数
1、拷贝构造的特征
(1)拷贝构造是构造函数的一种重载。
(2)只有一个成员,就是该类的一个引用。函数名如: test06(test06 & a)(test06是一个类)
如:
class test06
{
public:test06(int a,int b):_a(a),_b(b){}//拷贝构造test06(test06& a){_a = a._a;_b = a._b;}void Print(){cout << _a << " " << _b<<endl;}private:int _a ;int _b ;
};int main()
{test06 a(20,20);//test06 b(a); 与下面等价test06 b = a;a.Print();b.Print();return 0;
}
2、需要注意的点
(1)如果传的参数不是引用会发生什么?
答:会出现无尽递归。
(2)当使用编译器生成的默认拷贝构造函数时会怎么样?
对内置类型:进行值拷贝。
对自定义类型:调用其拷贝构造函数。
class A
{
public:A(int a) :_a(a){}//拷贝构造A(A& a){cout << "A(A& a)" << endl;}int _a;};class test06
{
public:test06(A a, int b) :_a(a), _b(b){}
private://自定义类型A _a;//内置类型int _b;
};int main()
{A a(10);test06 b(a, 20);test06 c(b);return 0;
}
(3)浅拷贝和深拷贝
浅拷贝也叫值拷贝就是按照字节序的方式直接进行拷贝
如:
test06(test06 &a){_a = a._a;_b = a._b;}
下面那样还能用浅拷贝完成吗?
class test07
{
public:test07(int* a):_a(a){}~test07(){delete[]_a;}test07(test07& p){_a = p._a;}
private:int* _a;
};int main()
{int* a = new int[10];for (int i = 0; i < 10; i++){a[i] = i;}test07 p(a);test07 pp(p);return 0;
}
答案是:不能的,因为当p._a
和pp._a
指的是同一块空间,当p
析构之后,p._a
指向的空间就被释放了,当pp
再使用pp._a
时就会出现问题,以及pp
析构时又会对这块空间进行析构,这样就会造成重复释放导致错误。
为了解决这个问题,我们使用深拷贝
如:
test07(test07& p)
{//重新开辟空间int* _a = new int[10];//拷贝memcpy(_a, p._a, sizeof(int)*10);}
总结:
当遇到需要申请空间之类的成员变量时,需要重写拷贝构造函数并使用深拷贝,不然使用系统默认生成的也可以。
3、作用
初始化对象。
四、重载赋值操作符
赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
返回类型 :
const 类名 &
返回类的话就可以实现连续赋值了。
函数名:operator=
。
参数类型:(const & 类名)
,因为编译器自动会传一个隐含的this
,所以我们传一个参数就够了。
实现:
class Kind
{
public://构造函数Kind(int a = 10,int b = 10){_a = a;_b = b;}//重载 = //只能作为成员函数 const 防止被修改const Kind & operator=(const Kind& p) {this->_a = p._a;this->_b = p._b;//返回 *this 使其可以连续赋值return *this; }
private:int _a;int _b;
};int main()
{Kind p1(20, 20);Kind p2;Kind p3;//连续赋值p3 = p2 = p1;cout << "p1: " << p1._a << " " << p1._b << endl;cout << "p2: " << p2._a << " " << p2._b << endl;cout << "p3: " << p3._a << " " << p3._b << endl;return 0;
}
其实默认的赋值运算符重载函数就是像上写的那样进行赋值,我们对于这样的拷贝叫做浅拷贝,这样做有一个弊端就是如果遇到动态申请的空间的话就有可能发生程序崩溃,这是因为共用一块空间当另一个对象将这块空间释放之后被赋值的那个对象再使用这块空间时就会发生崩溃。
如:
class Kind
{
public://构造函数Kind(int a = 10,int b = 10){_a = a;_b = b;arr = new int;*arr = a; }//重载 = //只能作为成员函数 const 防止被修改const Kind & operator=(const Kind& p) {this->_a = p._a;this->_b = p._b;this->arr = p.arr;//返回 *this 使其可以连续赋值return *this; }private:int _a;int _b;int* arr;
};int main()
{Kind p1(20, 20);Kind p2 = p1;return 0;
}
当上述的p1将arr释放了,p2再使用arr就会发生崩溃。
当我们遇到这种情况时我们使用深拷贝。
//深拷贝
const Kind& operator=(const Kind& p)
{this->_a = p._a;this->_b = p._b;int* tmp = new int;if (tmp == nullptr)exit(-1);*tmp = *p.arr;this->arr = tmp;//返回 *this 使其可以连续赋值return *this;
}
赋值运算符重载和拷贝构造的区别
拷贝构造是一个已经存在的类给一个刚创建的类进行初始化。
赋值运算符重载是一个已经存在的的另一个已经存在的类赋值。
如:
A a;
//只有a已经存在 使用拷贝构造
A b = a;A c;
A d;
//c\d都是已经存在的了, 使用赋值运算符重载
c = d;
总结:
当遇到动态申请的空间时需要重写赋值运算符,如果没有用编译器自动生成的即可。
五、取地址及const取地址操作符重载
1、const成员
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数
隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
如:
class test08
{
public:void Print() const //等同与 const * this {cout << _a;}
private:int _a;};
(1)const对象可以调用非const成员函数吗?
答:不可以,属于权限放大了。(2)非const对象可以调用const成员函数吗?
答:可以,属于权限缩小。
(3)const成员函数内可以调用其它的非const成员函数吗?
答:不可以,属于权限放大了。(4) 非const成员函数内可以调用其它的const成员函数吗?
答:可以,属于权限缩小。
2、取地址及const取地址操作符重载
这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
class test08
{
public ://取地址test08* operator&(){return this ;}// const取地址const test08* operator&()const{return this ;}
};
相关文章:

c++ - 类的默认成员函数
文章目录 前言一、构造函数二、析构函数三、拷贝构造函数四、重载赋值操作符五、取地址及const取地址操作符重载 前言 默认成员函数是编译器自动生成的,也可以自己重写,自己重写之后编译器就不再生成,下面是深入了解这些成员函数。 一、构造…...
Java哈希查找(含面试大厂题和源码)
哈希查找(Hash Search)是一种基于哈希表(Hash Table)的数据查找方法。哈希表通过使用哈希函数将键(Key)映射到表中的位置来存储数据,从而实现快速的数据访问。哈希查找的效率通常取决于哈希函数…...

c++中常用库函数
大小写转换 islower/isupper函数 char ch1 A; char ch2 b;//使用islower函数判断字符是否为小写字母 if(islower(ch1)){cout << ch1 << "is a lowercase letter." << end1; } else{cout << ch1 << "is not a lowercase lette…...

Scrapy框架 进阶
Scrapy框架基础Scrapy框架进阶 【五】持久化存储 命令行:json、csv等管道:什么数据类型都可以 【1】命令行简单存储 (1)语法 Json格式 scrapy crawl 自定义爬虫程序文件名 -o 文件名.jsonCSV格式 scrapy crawl 自定义爬虫程…...

ubuntu22安装snipaste
Ubuntu 22.04 一、Snipaste 介绍和下载 Snipaste 官网下载链接: Snipaste Downloads 二、安装并使用 Snipaste # 1、进入Snipaste-2.8.9-Beta-x86_64.AppImage 目录(根据自己下载目录) cd /home/jack/Downloads/softwares/AppImage# 2、Snipaste-2.8.9-…...

spring-cloud微服务openfeign
Spring Cloud openfeign对Feign进行了增强,使其支持Spring MVC注解,另外还整合了Ribbon和Nacos,从而使得Feign的使用更加方便 优势,openfeign可以做到使用HTTP请求远程服务时就像洞用本地方法一样的体验,开发者完全感…...

小程序变更主体需要多久?
小程序迁移变更主体有什么作用?小程序迁移变更主体的好处有很多哦!比如可以获得更多权限功能、公司变更或注销时可以保证账号的正常使用、收购账号后可以改变归属权或使用权等等。小程序迁移变更主体的条件有哪些?1、新主体必须是企业主体&am…...

19 Games101 - 笔记 - 相机与透镜
**19 ** 相机与透镜 目录 摘要一 照相机主要部分二 小孔成像与视场(FOV)三 曝光(Exposure)四 景深(Depth of Field)总结 摘要 虽说照相机与透镜属于相对独立的话题,但它们的确是计算机图形学当中的一部分知识。在过往的十多篇笔记中,我们学习的都是如…...

Flink入门学习 | 大数据技术
⭐简单说两句⭐ ✨ 正在努力的小新~ 💖 超级爱分享,分享各种有趣干货! 👩💻 提供:模拟面试 | 简历诊断 | 独家简历模板 🌈 感谢关注,关注了你就是我的超级粉丝啦! &…...

Arthas实战教程:定位Java应用CPU过高与线程死锁
引言 在Java应用开发中,我们可能会遇到CPU占用过高和线程死锁的问题。本文将介绍如何使用Arthas工具快速定位这些问题。 准备工作 首先,我们创建一个简单的Java应用,模拟CPU过高和线程死锁的情况。在这个示例中,我们将编写一个…...

HTML制作跳动的心形网页
作为一名码农 也有自己浪漫的小心思嗷~ 该网页 代码整体难度不大 操作性较强 祝大家都幸福hhhhh 效果成品: 全部代码: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML><HEAD><TITLE> 一个…...

如何在Odoo 17 销售应用中使用产品目录添加产品
Odoo,作为一个知名的开源ERP系统,发布了其第17版,新增了多项功能和特性。Odoo 17包中的一些操作简化了,生产力提高了,用户体验也有了显著改善。为了为其用户提供新的和改进的功能,Odoo不断进行改进和增加新…...
为什么pdf拆分出几页之后大小几乎没有变化
PDF 文件的大小在拆分出几页之后几乎没有变化可能有几个原因: 图像压缩: 如果 PDF 文件中包含图像,而这些图像已经被压缩过,拆分后的页面依然会保留这些压缩设置,因此文件大小可能不会显著变化。 文本和矢量图形: PDF 文件中的文…...

如何在 VM 虚拟机中安装 OpenEuler 操作系统保姆级教程(附链接)
一、VMware Workstation 虚拟机 若没有安装虚拟机的可以参考下篇文章进行安装: 博客链接https://eclecticism.blog.csdn.net/article/details/135713915 二、OpenEuler 镜像 点击链接前往官网 官网 选择第一个即可 三、安装 OpenEuler 打开虚拟机安装 Ctrl …...

(六)PostgreSQL的组织结构(3)-默认角色和schema
PostgreSQL的组织结构(3)-默认角色和schema 基础信息 OS版本:Red Hat Enterprise Linux Server release 7.9 (Maipo) DB版本:16.2 pg软件目录:/home/pg16/soft pg数据目录:/home/pg16/data 端口:57771 默认角色 Post…...
DockerFile定制镜像
dockerfile 简介 Dockerfile 是⼀个⽤来构建镜像的⽂本⽂件,⽂本内容包含了⼀条条构建镜像所需的指令和 说明,每条指令构建⼀层,最终构建出⼀个新的镜像。 docker镜像的本质是⼀个分层的⽂件系统 centos的iso镜像⽂件是包含bootfs和rootfs…...

Java8中JUC包同步工具类深度解析(Semaphore,CountDownLatch,CyclicBarrier,Phaser)
个人主页: 进朱者赤 阿里非典型程序员一枚 ,记录平平无奇程序员在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法(公众号同名) 引言 在Java中,并发编程一直是一个重要的领域,而JDK 8中的java.u…...
岛屿个数(dfs)
[第十四届蓝桥杯省B 岛屿个数] 小蓝得到了一副大小为 M N MN MN 的格子地图,可以将其视作一个只包含字符 0 0 0(代表海水)和 1 1 1(代表陆地)的二维数组,地图之外可以视作全部是海水,每个岛…...
【C++造神计划】运算符
1 赋值运算符 赋值运算符的功能是将一个值赋给一个变量 int a 5; // 将整数 5 赋给变量 a 运算符左边的部分叫作 lvalue(left value),右边的部分叫作 rvalue(right value) 左边 lvalue 必须是一个变量 右边 rval…...

Cortex-M3/M4处理器的bit-band(位带)技术
ARM Cortex-M3/M4的位带(Bit-Band)技术是一种内存映射技术,它允许对单个位进行直接操作,而不需要对整个字(通常是32位)进行操作。这项技术主要用于对特定的位进行高效的读写,特别是在需要对GPIO…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
高防服务器价格高原因分析
高防服务器的价格较高,主要是由于其特殊的防御机制、硬件配置、运营维护等多方面的综合成本。以下从技术、资源和服务三个维度详细解析高防服务器昂贵的原因: 一、硬件与技术投入 大带宽需求 DDoS攻击通过占用大量带宽资源瘫痪目标服务器,因此…...

6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...