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)注意

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化: 引用成员变量 const成员变量 自定义类型成员(且该类没有默认构造函数时)
  3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
  4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
//初始化列表
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._app._a指的是同一块空间,当p析构之后,p._a指向的空间就被释放了,当pp再使用pp._a时就会出现问题,以及pp析构时又会对这块空间进行析构,这样就会造成重复释放导致错误。
在这里插入图片描述

为了解决这个问题,我们使用深拷贝
如:
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/eee7e22a6f9a463c97c857206c9dc85e.png

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 ;}
};

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

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

相关文章

【面试八股总结】排序算法(一)

参考资料 &#xff1a;阿秀 一、冒泡排序 冒泡排序就是把小的元素往前交换或者把大的元素往后交换&#xff0c;比较相邻的两个元素&#xff0c;交换也发生在这两个元素之间。具体步骤&#xff1a; 比较相邻的元素。如果第一个比第二个大&#xff0c;就交换他们两个。对每一对…

HCIE考试第六题:规划设计

文章目录 业务个性化配置题目与做题步骤如下6规划设计6.1模板说明6.1.1规划设计图模板6.1.2.集成设计LLD模板6.2 华为云Stack规划设计画图【多Region组网】6.2.1.多Region说明和画图说明6.2.2.核心交换机画线6.2.3.TOR交换机画线6.2.4.防火墙画线6.2.5.业务区连线6.2.5.1.业务和…

MGRE-OSPF接口网络类型实验

OSPF接口网络类型实验 一&#xff0c;实验拓扑 初始拓扑&#xff1a; 最终拓扑&#xff1a; 二&#xff0c;实验要求及分析 要求&#xff1a; 1&#xff0c;R6为ISP只能配置IP地址&#xff0c;R1-R5的环回为私有网段 2&#xff0c;R1/R4/R5为全连的MGRE结构&#xff0c;R…

二叉树的顺序存储结构

定义一个长度为MaxSize的数组t&#xff0c;按照从上至下、从左至右的顺序依次存储完全二叉树中的各个结点。 TreeNode t[MaxSize]; #define MaxSize 100 struct TreeNode {ElemType value; //结点中的数据元素bool isEmpty; //结点是否为空 };for (int i0; i<…

Oracle 数据库 count的优化-避免全表扫描

Oracle 数据库 count的优化-避免全表扫描 select count(*) from t1; 这句话比较简单&#xff0c;但很有玄机&#xff01;对这句话运行的理解&#xff0c;反映了你对数据库的理解深度&#xff01; 建立实验的大表他t1 SQL> conn scott/tiger 已连接。 SQL> drop table …

ubuntu22安装snipaste

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

Spring框架第一篇(Spring概述与IOC思想)

文章目录 一、Spring概述二、Spring家族三、Spring Framework四、IOC思想五、IOC容器在Spring中的实现 一、Spring概述 Spring 是最受欢迎的企业级 Java 应用程序开发框架&#xff0c;数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。…

STM32之FreeRTOS移植

1.FreeRTOS的移植过程是将系统需要的文件和代码进行移植和裁剪&#xff0c;其移植的主要过程为&#xff1a; &#xff08;1&#xff09;官网上下载FreeRTOS源码&#xff1a;https://www.freertos.org/ &#xff08;2&#xff09;移植文件夹&#xff0c;在portable文件夹中只需…

Flask框架——安装与第一个应用

安装 Flask是一个轻量级的Python Web框架。它是一个微型框架&#xff0c;具有灵活性和可扩展性。Flask使用Python语言编写&#xff0c;它是一个开源框架&#xff0c;使得它可以自由地使用和修改。Flask框架可以用于构建任何类型的Web应用程序&#xff0c;包括单页面应用程序、…

003 【笔记神器】Obsidian:打造属于自己的万能工作台

前言&#xff1a;Obsidian 是一款很多大神都在用的笔记软件&#xff0c;具有强大的功能&#xff0c;能够满足日常各种笔记的需求。强大之处在于&#xff1a;Obsidian 能够安装各种强大的插件&#xff0c;实现各种功能。 废话不多说&#xff0c;玩转 Obsidian 仅需这篇文章足矣&…

护眼落地灯哪个牌子好?高分榜前五的护眼大路灯汇总!

落地灯面世之后就收到了诸多好评&#xff0c;但与此矛盾的是&#xff0c;每年都有大量关于光线不好刺眼的信息&#xff0c;长久使用下来眼睛疲劳、酸痛&#xff0c;根本达不到改善光线环境减少视觉疲劳的效果。不过大家无需过分忧虑&#xff0c;因为光线不好的问题问题主要是不…

前端Vue3+uni+Ts

本次记录小兔仙仙的制作过程。 先看下我们的项目截图。主要是手机端&#xff0c;这里用了uniappVScode.三端适配的。可以打包成安卓和苹果。微信小程序。 首先&#xff1a;创建一个uni新的ts项目。 # 通过 git 从 gitee 克隆下载 登录 - Gitee.com git clone -b vite-ts http…

构建跨设备3D应用:HOOPS的跨平台开发能力

在当今数字化和可视化需求不断提升的时代&#xff0c;三维技术的应用越来越广泛&#xff0c;尤其在制造、建筑、工程及媒体行业。HOOPS&#xff0c;由Tech Soft 3D开发&#xff0c;是一套全面的软件开发工具包&#xff0c;用于构建高性能的三维应用程序。该工具包涵盖了从三维渲…

Centos7在线安装mysql5.7

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 安装Mysql yum源1、卸载旧环境2、下载mysql yum源3、上传到自己服务器1&#xff09;、上传源2&#xff09;、安装yum源3&#xff09;、查看yum源是否安装成功 安装M…

信息系统项目管理师——管理类计算

风险管理——风险曝光度 风险曝光度概率*影响&#xff0c;概率指风险发生的概率&#xff0c;影响指风险一旦发生&#xff0c;受到影响的项。 题号【GX20061101](61) 知识点[风险曝光度] 风险的成本估算完成后&#xff0c;可以针对风险表中每个风险计算其风险曝光度。某软件小…

面试官:MySQL的自增 ID 用完了,怎么办?

如果你用过或了解过MySQL&#xff0c;那你一定知道自增主键了。每个自增id都是定义了初始值&#xff0c;然后按照指定步长增长&#xff08;默认步长是1&#xff09;。虽然&#xff0c;自然数是没有上限的&#xff0c;但是我们在设计表结构的时候&#xff0c;通常都会指定字段长…

0.25W 3KVDC 隔离单、双输出 DC/DC SMD 型电源模块 ——TPVT-W2 系列

TPVT-W2系列是一款标准的表面贴装电源模块&#xff0c;完全实现采用全自动贴片机来组装和满足回流焊工艺&#xff0c;大大提高产能和降低人工费用。此系列产品小&#xff0c;效率高&#xff0c;低输出纹波及提供3000V以上的直流电压隔离&#xff0c;SMD封装。

从数据中台到上层应用全景架构示例

一、前言 对于大型企业而言&#xff0c;数据已经成为基本的生产资料&#xff0c;但是有很多公司还是值关心上层应用&#xff0c;而忽略了数据的治理&#xff0c;从而并不能很好的发挥公司的数据资产效益。比如博主自己是做后端的&#xff0c;主要是做应用层&#xff0c;也就是…

计算机网络 Cisco路由信息协议(RIP)实验

一、实验内容 1、命名 2、关闭域名解释 3、设置路由器接口IP地址 4、根据要求配置RIP以实现所有客户机都能相互通信 5、配置默认路由 二、实验数据处理 1、建立拓扑图 2、PC机地址配置 主机IP地址子网掩码网关PC110.23.1.2255.255.255.010.23.1.1PC210.23.1.3255.255.2…

OR36 链表的回文结构

描述 对于一个链表&#xff0c;请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法&#xff0c;判断其是否为回文结构。 给定一个链表的头指针A&#xff0c;请返回一个bool值&#xff0c;代表其是否为回文结构。保证链表长度小于等于900。 测试样例&#xff1a; 1->…