c++构造和析构

news2024/11/15 21:12:58

在这里插入图片描述

1.构造函数

1.构造函数特性

  1. 构造函数名字和类名相同
  2. 构造函数没有返回值(void有返回值,返回值为空)
  3. 不写构造函数,每一个类中都存在默认的构造函数,默认的构造函数是没有参数的
  4. default显示使用默认的构造函数
  5. delete删掉默认函数
  6. 当我们自己写了构造函数,默认的构造函数就不存在
  7. 构造函数是不需要自己调用,在构造函数对象的时候自己调用
  8. 构造函数决定了对象的长相
  9. 无参构造函数可以构造无参对象
  10. 有参构造函数,对象必须要带有参数
  11. 构造函数允许被重载和缺省
  12. 构造函数一般情况是公有属性
  13. 构造函数一般是用来给数据初始化
  14. 构造函数允许调用另一个构造函数,但是必须采用初始化参数列表的写法:
  • 构造函数的初始化参数列表: 构造函数名(参数1,参数2,…):成员1(参数1),成员2(参数2),…{}

2.综合代码

#include<iostream>
#include<string>
using namespace std;
class MM
{
public:
	//构造函数
	//MM()=default;          //使用的是默认的构造函数
	//MM()=delete;
	MM()
	{
		cout<<"无参构造函数"<<endl;
	}
	MM(int a)
	{
		cout<<"具有一个参数的构造函数"<<endl;
	}
protected:
};

class Girl
{
public:
	Girl()=delete;
protected:
};

class Student
{
public:
	Student(){m_age=15,m_name="k";}
	Student(string name,int age)
	{
		//做初始化操作
		m_name=name;
		m_age=age;
	}
	void printStudent()
	{
		cout<<m_name<<"\t"<<m_age<<endl;
	}
protected:
	string m_name;
	int m_age;
};

class Test{
public:
	//构造函数特殊写法
	Test(int a,int b):a(a),b(b){}
	Test():Test(9,8){}             //无参构造函数调用有参构造函数,构造委托
	//Test()=default;
	void print()
	{
		cout<<a<<"\t"<<b<<endl;
	}
protected:
	int a=0;
	int b=0;
};

struct Data
{
	int a;
	int b;
	int c;
	Data(int a):a(a){}
	Data(int a,int b,int c):a(a),b(b),c(c){
		cout<<"调用三个参数的构造函数"<<endl;
	}
	void print(){
		cout<<a<<"\t"<<b<<"\t"<<c<<endl;
	}
};
int main()
{
	MM boy;
	MM girl(1);
	
	//Girl girl;    //默认构造函数已经删除,且自己没写构造函数,所以错误
	
	//普通对象
	Student mm("zhangkai",15);
	mm.printStudent();
	
	//new一个对象,new的过程是先在自由存储区创建一个无名对象,再把地址返回
	Student* pstu = new Student("zhi",29); 
	pstu->printStudent();
	
	//对象数组
	Student stuarry[3]; //无名对象,需要无参构造函数,否则错误
	stuarry[1].printStudent();
	stuarry[2].printStudent();
	stuarry[0].printStudent();
	
	//初始化参数列表
	Test test(99,88);
	test.print();
	Test bb;
	bb.print();
	Test xx={88,99};  //这个过程也是调用构造函数过程,{}中数据个数要和构造函数参数一致
	xx.print();
	
	Data oo(3);
	oo.print();
	Data data(1,2,3);
	data.print();
}

2.析构函数

1.析构函数特性

  1. 函数名等于~加上类名
  2. 析构函数没有参数,所以析构函数不能被重载也不能被缺省
  3. 对象死亡(生命周期结束)的最后一个事情是调用析构函数
  4. 析构函数都是公有属性
  5. 什么时候写析构函数?
  • 当类的成员new了内存就需要自己手动写析构函数
  1. 不写析构函数,也会存在一个析构函数,但是不具有释放new的内存的功能

2.综合代码

#include<iostream>
using namespace std;
class MM
{
public:
	MM()
	{
		p=new int;
	}
	void freeMM()
	{
		delete p;
		p=nullptr;
	}
	~MM()
	{
		cout<<"我是析构函数"<<endl;
		delete p;
		p=nullptr;
	}
protected:
	int* p;
};
int main()
{
	{
		MM mm;
		//mm.freeMM();        //当然也可以自己写函数释放,不过要手动释放
		MM* p=new MM;
		delete p;            //立刻马上调用析构函数    
	}
	cout<<"..............."<<endl;
    return 0;
}

3.拷贝构造函数

1.拷贝构造函数特性

  1. 不写拷贝构造函数,存在一个默认拷贝构造函数
  2. 拷贝构造函数名和构造函数一样,算是构造函数特殊形态
  3. 拷贝构造函数唯一的一个参数就是对对象的引用
  • 普通引用
  • const引用
  • 右值引用——>移动拷贝
  1. 当我们通过一个对象产生另一个对象时候就会调用拷贝构造函数

2.综合代码

#include<iostream>
#include<string>
using namespace std;
class MM{
public:
	MM()=default;
	MM(MM& object)
	{
		cout<<"调用拷贝构造函数"<<endl;
	}
protected:
};

class Girl{
public:
	Girl(string name,int age):name(name),age(age){}
	Girl():Girl("",0){}
	Girl(const Girl& object)
	{	
		//拷贝构造函数就是通过一个对象赋值另一个对象
		name=object.name;
		age=object.age;
		cout<<"调用拷贝构造函数"<<endl;
	}
	void print()
	{
		cout<<name<<"\t"<<age<<endl;
	}
protected:
	string name;
	int age;
};

void printBoy(Girl girl)           //调用拷贝构造函数,Gilr girl=实参
{
	girl.print();
}

void printGirl(Girl& girl)
{
	girl.print();
}

void testGirl()
{
	Girl mm("小妹",19);
	Girl girl(mm);          //调用
	girl.print();
	Girl beauty=mm;           //调用
	beauty.print();
	cout<<"传入普通变量"<<endl;
	printBoy(girl);                //调用
	cout<<"传入引用"<<endl;        
	//不调用拷贝构造函数
	printGirl(girl);
	
	//匿名对象的拷贝构造函数,匿名对象是右值,可以用const或者右值引用
	//const里不可修改
	//右值引用里提供了可修改的接口
	Girl xx=Girl("zhangkai",19);
	xx.print();
}

class Boy{
public:
	Boy(string name,int age):name(name),age(age){}
	Boy(Boy&& object)
	{
		name=object.name;
		age=object.age;
		cout<<"右值引用的拷贝构造"<<endl;
	}
	Boy(Boy& object)
	{
		name=object.name;
		age=object.age;
		cout<<"普通的拷贝构造"<<endl;
	}
protected:
	string name;
	int age;
};

void testBoy(){
	Boy boy("boy",10);          
	Boy bb=boy;                   //调用普通对象
	Boy coolman=Boy("dasd",29);   //右值引用的拷贝构造函数
	//没有打印结果,IDE做了优化,看不到
}
int main()
{
	MM mm;
	MM girl=mm;               //会调用拷贝构造函数
	MM boy(girl);             //会调用拷贝构造函数
	//string str="dasd";
	//string str2(str);
	//string str3=str2;
	//string类型赋值实际上是调用拷贝构造函数
	
	//调用拷贝构造函数语句一定有类名
	//不调用拷贝构造函数,这是先创建对象,然后赋值,属于运算符重载
	MM npc;                    
	npc=girl; 
	
	cout<<"............"<<endl;	
	testGirl();
	cout<<"............"<<endl;
	testBoy();
	return 0;
}

3.深浅拷贝问题

深浅拷贝只在类中存在指针,并且做了内存申请的,才会存在引发析构问题(内存释放问题)

  • 默认的拷贝构造都是浅拷贝
  • 拷贝构造函数中做普通的赋值操作也是浅拷贝
错误代码
#include<iostream>
#include<cstring>
using namespace std;
class MM{
public:
	MM(const char* str,int num)
	{
		int length=strlen(str)+1;
		name=new char[length];
		strcpy_s(name,length,str);
		age=num;
	}
	~MM()
	{
		if(name!=nullptr)
		{
			delete[] name;
			name=nullptr;
		}
	}
protected:
	char* name;
	int age;
};
void testQuestion(){
	MM mm("zhangzhang",19);
	MM xx=mm;
}
int main() 
{
	testQuestion();
	return 0;
}

原因如下图:
在这里插入图片描述

正确代码
#include<iostream>
#include<cstring>
using namespace std;
class MM {
public:
	MM(const char* str, int num)
	{
		int length = strlen(str) + 1;
		name = new char[length];
		strcpy_s(name, length, str);
		age = num;
	}
	MM(const MM& object)
	{
		//深拷贝
		int length = strlen(object.name) + 1;
		name = new char[length];
		strcpy_s(name, length, object.name);
		age = object.age;
	}
	~MM()
	{
		if (name != nullptr)
		{
			delete[] name;
			name = nullptr;
		}
	}
protected:
	char* name;
	int age;
};
void testQuestion() {
	MM mm("zhangzhang", 19);
	MM xx = mm;
}
int main()
{
	testQuestion();
	return 0;
}

解释:

在这里插入图片描述

4.构造和析构的顺序问题

  1. 一般情况构造顺序和析构顺序是相反的(先构造后释放,后构造先释放)
  2. new对象,调用delete直接被释放
  3. static对象,最后释放(生命周期最长)
#include<iostream>
#include<string>
using namespace std;
class MM
{
public:
	MM(string info="A"):info(info){cout<<info;}
	~MM(){cout<<info;}
protected:
	string info;
};
void testOrder()
{
	MM mm1("B");
	static MM mm2("C");
	MM* p=new MM("D");
	delete p;
	MM arr[3];
}

int main()
{
	testOrder();
	return 0;
}

5.c++类的组合

介绍:

  • 一个类包含另一个类的对象为数据成员叫做类的组合。当多种事物是一个事物的一部分,采用组合类来完成描述,c++中组合的使用优先于继承
  • 注意:类不能包含自身对象,否则会形成死循环
  • 组合类的构造函数,必须要采用初始化参数列表的方式调用分支类的构造函数
  • 组合类的构造顺序:先构造分支类,分支类的顺序只和声明顺序有关,和初始化参数列表一点毛线关系
#include<iostream>
#include<string>
using namespace std;

class MM
{
public:
	MM(){cout<<"构造mm"<<endl;}
	MM(string name):name(name){}
	void print(){cout<<"MM:"<<name<<endl;}
protected:
	string name;
};

class GG
{
public:
	GG(){cout<<"构造gg"<<endl;}
	GG(string name,int age):name(name),age(age){}
	void print(){cout<<name<<"\t"<<age<<endl;}
protected:
	string name;
	int age;
};

class Family
{
public:
	Family(string mmName,string ggName,int age):mm(mmName),gg(ggName,age){}
	//但是分支类中必须存在无参的构造函数
	Family(){cout<<"构造组合类"<<endl;}
	void print(){gg.print();mm.print();}
protected:
	MM mm;
	GG gg;
};
int main()
{
	Family dd("mm","gg",19);
	dd.print();
	Family object;
	object.print();    //先分支再组合,且分支的顺序与声明的顺序一致
	return 0;
}

6.c++类中类

  • 类中类的访问问题以及类中类先申明后定义的写法
  • 类中类依旧受权限限定
  • 访问必须要类名::剥洋葱的方式访问
#include<iostream>
using namespace std;
class xx {
public:
	xx(){cout<<"外面的构造函数"<<endl;}
protected:
public:
	//类中类依旧受权限限定,就相当于把一个类丢到另外一个类中,他们两个没有关系
	class dd
	{
	public:
		dd()
		{
			cout<<"类中类构造函数"<<endl;
		}
		void print(){cout<<"类中类构造函数"<<endl;}
	protected:
	};
};
void testlzl()
{
	xx::dd bb;
	bb.print();
}
int main()
{
	testlzl();
	return 0;
}

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

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

相关文章

MM采购订单及发票相关后台表介绍(图解)

EKPO 采购凭证项目 EKKO 采购凭证抬头 EORD 采购货源清单 EINA 采购信息记录 - 一般数据 EINE 采购信息记录 - 采购组织数据 EKET 计划协议计划行 EKES 供应商确认 EKKN 采购凭证中的帐户设置 EKBE 采购凭证历史 EKBZ 每个采购凭证的历史&#xff1a;交货费用 RBKP 凭…

在wsl下开发T113的主线linux(2)-编译awboot

意外发现有awboot能够代替uboot直接引导内核&#xff0c;体验了一下果断选择awboot&#xff0c;因为足够简洁&#xff0c;编译大小只有32k&#xff0c;和uboot接近1M的体量相比&#xff0c;简直是小而美&#xff0c;启动速度也比uboot快上不少&#xff0c;也能同时支持sd卡&…

DoIP协议从入门到精通系列——车载网络拓扑

因特网协议(IP-Internet protocol)是互联网规范中的基本协议,它仅是支持互联网正常运转“TCP/IP”协议簇之一。UDP协议也是TCP/IP协议体系中的内容(因为名称中只含有TCP/IP名称,往往会忽略UDP)。以太网引入到车载网络后,汽车也会慢慢进入车联网时代(或者物联网,万物互…

aws codepipeline 在pipeline构建过程中使用变量

参考资料 Action structure reference codebuild构建环境中的环境变量 codepipeline中的变量 在codePipeline中使用变量 对于codepipeline来说&#xff0c;管道结构中的每个操作都有自身的结构和定义&#xff0c;本文主要讨论不同资源的输出变量。 基本概念 变量允许用户…

数据完整性(一)

目录 数据完整性&#xff1a; 什么是数据完整性&#xff1a; 数据完整性的类型 1&#xff1a;实体完整性 2&#xff1a;域完整性&#xff1a; 3、引用完整性&#xff1a; 4、自定义完整性&#xff1a; 完整性约束&#xff1a; 数据完整性的实现方式&#xff1a; 实体完整性&a…

抽象⼯⼚模式

抽象⼯⼚模式 1.抽象工厂模式介绍 抽象⼯⼚模式与⼯⼚⽅法模式虽然主要意图都是为了解决&#xff0c;接⼝选择问题。但在实现上&#xff0c;抽象⼯⼚是⼀ 个中⼼⼯⼚&#xff0c;创建其他⼯⼚的模式。 2.案例场景模拟 2.1场景简述 很多时候初期业务的蛮荒发展&#xff0c;也…

NLP中隐性语义分析及奇异值分解(SVD)-学习笔记

目录 1、隐性语义分析 2、奇异值分解 2.1 左奇异向量U 2.2 奇异值向量S 2.3 右奇异值向量V^T 2.4 SVD矩阵的方向 2.5 主题约简 1、隐性语义分析 隐形语义分析基于最古老和最常用的降维技术–奇异值分解(SVD)。SVD将一个矩阵分解成3个方阵&#xff0c;其中一个是对角矩阵…

网络类型实验报告

实验拓扑 实验要求 1.R2为ISP&#xff0c;其上只能配置IP地址 2.R1-R2之间为HDLC封装 3.R2-R3之间为ppp封装&#xff0c;pap认证&#xff0c;R2为主认证方 4.R2-R4之间为PPP封装&#xff0c;chap认证&#xff0c;R2为主认证方 5.R1、R2、R3构建MGRE环境&#xff0c;仅R1P地址固…

【C++】-- C++11基础常用知识点

目录 C11简介 统一的列表初始化 &#xff5b;&#xff5d;初始化 std::initializer_list std::initializer_list使用场景&#xff1a; 声明 auto decltype nullptr 范围for循环 STL中一些变化 新容器 array容器 forward_list容器 容器中的一些新方法 C11简介 在…

TDK | RoboKit1-DK 开发平台为实现未来机器人的多样性扫清障碍

机器人行业正在飞速发展&#xff0c;新技术亦层出不穷。TDK 现推出了 TDK RoboKit1-DK 机器人开发平台&#xff0c;该平台配备了机器人精确操作所必需的各种传感器&#xff0c;有助于在各种机器人开发中提高效率和性能。 在技术快速进步与成长的环境中&#xff0c;机器人开发所…

计算机组成原理实验——二、寄存器实验

一、实验目的 1.掌握寄存器堆的工作原理和接口。 2.掌握寄存器堆的实现方法。 3.掌握寄存器堆在微处理器中承担的功能。 二&#xff0e;实验内容 设计一32*32bit 的寄存器文件&#xff0c;即32 个 32 位的寄存器文件&#xff08;寄存器组&#xff09; –具备两组读端口及一组…

【王道操作系统】2.2.1 处理机调度的概念与层次

处理机调度的概念与层次 文章目录处理机调度的概念与层次1.调度的基本概念2.调度的三个层次2.1 高级调度(作业调度)2.2 中级调度(内存调度)2.3 进程的挂起状态与七状态模型2.4 低级调度(进程调度)2.5 三层调度的联系和对比1.调度的基本概念 2.调度的三个层次 2.1 高级调度(作业…

GroundTrue和里程计输出的位姿的参考坐标系不一致的情况

这里写目录标题前言数据集描述使用TF工具包获取使用Eigen库计算置换输出误差对比没做转换之前转换之后前言 最近遇到一个数据集的ground true参考坐标和vSLAM输出的位姿的参考坐标不一样的问题&#xff0c;记录一下。 在之前参加的一个PRCV 2022的多传感器融合SLAM挑战赛中也同…

MATLAB读取tif格式图像

tif格式数据本质上就是带有地理信息的矩阵数据。 geotiffread函数 MATLAB帮助-geotiffread 语法Syntax&#xff1a; [A,R] geotiffread(filename) [X,cmap,R] geotiffread(filename) [A,refmat,bbox] geotiffread(filename) [X,cmap,refmat,bbox] geotiffread(filenam…

Symbol详解

Symbol Symbol是es6引入的一个新的原始数据类型&#xff0c;是一个独一无二的值。 目前为止&#xff0c;js的数据类型有以下几种&#xff1a; 数据类型说明undefinedundefinednullnullboolean布尔值string字符串number数字Bigint大整数Object对象SymbolSymbol Symbol通过Symb…

[go学习笔记.第十八章.数据结构] 2.约瑟夫问题,排序,栈,递归,哈希表,二叉树的三种遍历方式

一.约瑟夫问题 josephu 问题: 设编号为1, 2 &#xff0c;... n 的n个人围坐一圈, 约定编号为 k (1<k<n &#xff09;的人从 1 开始报数&#xff0c;数到m的那个人出列&#xff0c;它的下一位又从1开始报数&#xff0c;数到 m 的那个人又出列&#xff0c;依次类推&#xf…

ActivityManagerService

1 AMS 家族 ActivityManagerService&#xff08;AMS&#xff09;主要负责系统中四大组件的启动、切换、调度以及应用程序的管理和调度工作&#xff0c;其职责与操作系统中的进程管理和调度模块类似。ActivityManagerService 进行初始化的时机很明确&#xff0c;就是在 system_…

计算机组成原理“上分秘籍”——数据的表示和运算

前言 上分地图 目录 前言 上分地图 猜你想问 Q1&#xff1a;为何要研究数据表示问题&#xff1f; Q2&#xff1a;什么叫数据表示&#xff1f;计算机中又有哪些方法&#xff1f; 正文 上分突破口1&#xff1a;进位计数法 例&#xff1a;二进制转为十进制 例&#xff…

路由综合实验

目录需求分析解决方法1&#xff0c;环回配置2&#xff0c;路由接口ip配置3&#xff0c;配置DHCP服务4&#xff0c;配置缺省5&#xff0c;静态路由配置6.浮动路由配置7&#xff0c;nat的配置8&#xff0c;远程服务及端口进行映射需求分析 一&#xff0c;原始需求如图&#xff1…

基于React Native开发的非法App破解记录

目标app YUhSMGNITTZMeTloWm1ZdWJIVnNkWE5wY2k1dFpTOD0 使用jadx反编译&#xff0c;找了一圈没有找到相应代码&#xff0c;看AndroidManifest.xml也不像有加壳的样子。。。 在lib目录下找到libreactnativejni.so文件&#xff0c;看似和react相关&#xff0c;莫非这app是大前端…