c++ 构造函数与析构函数

c++构造函数:

(1)构造函数名必须与类名相同

(2)无返回值

(3)访问权限符一般设置为public

(4)无自定义构造函数,编译器提供默认构造函数,构造函数只调用一次

c++析构函数:

(1)析构函数与类名相同,前面加~符号

(2)析构函数没有参数,不能重载,一个类里只有一个析构函数

(3)析构函数无返回值

(4)无自定义析构函数,编译器提供默认析构函数

当程序结束时,编译器会自动调用析构函数完成对象的清理工作,如果类中没有定义析构函数,编译器会提供一个默认的析构函数,但默认的析构函数只能完成栈内存对象的资源清理,无法完成堆内存堆对象的清理。因此需要自定义析构函数,析构函数的调用情况由以下几种

(1)在一个函数中自定义了一个对象,当函数调用结束时,对象应当被立即释放,对象释放之前编译器会调用析构函数释放资源。

(2)对于static修饰的对象和全局对象,只有在程序结束时编译器才会自动调用析构函数。

(3)对于new运算符创建的对象,在调用delete释放时,编译器会调用析构函数释放资源。

注意:析构函数的调用顺序与构造函数的调用顺序是相反的。在构造对象和析构对象时,c++遵循先构造的后析构,后构造的先析构。(栈)

#include <iostream>
using namespace std;
#include <iostream>
class Person {
private:
	string name;
	int age;
public:
	Person(string name);
	~Person();
};
Person::Person(string name) {
	this->name = name;
	cout << "调用person的构造函数:"<<name << endl;
}
Person::~Person() {
	cout << "调用person的析构函数:" <<name<< endl;
}
int main() {
	Person person1("1");
	Person person2("2");
	return 0;
}

 c++拷贝构造函数:

拷贝构造函数是一种特殊的构造函数,它具有构造函数的所有特性,并且使用本类对象的引用作为形参,能够通过一个已经存在的对象初始化该类的另一个对象,为了使引用的对象不被修改,通常使用const修饰引用的对象。

拷贝构造函数的调用情况

(1)使用一个对象初始化另外一个对象

(2)对象作为参数传递给函数。当函数的参数作为对象时,编译器会调用拷贝构造函数将实参传递给形参

(3)函数返回值为对象。当函数返回值为对象时,编译器会调用拷贝构造函数将返回值复制到临时对象传出

示例:(1)这是使用一个对象初始化另外一个对象,只完成简单的赋值操作,即浅拷贝

#include <iostream>
using namespace std;
#include <iostream>
class Person {
private:
	string name;
	int age;
public:
	Person(string name, int age);
	Person(const Person& person);
	~Person();
};
Person::Person(string name, int age) {
	this->name = name;
	this->age = age;
	cout << "调用person的构造函数:" << name << endl;
}
Person::Person(const Person& person) {//拷贝构造函数
	name = person.name;
	age = person.age;
	cout << "调用拷贝构造函数" << endl;
}
Person::~Person() {
	cout << "调用析构函数" << name << " " << age << endl;
}
int main() {
	Person person1("张三", 18);
	Person person2(person1);
	return 0;
}

使用匿名对象赋值


int main() {
	Person person1 = Person("张三",18);
	//不要利用拷贝构造函数初始化匿名对象 如
	//Person(person1);编译器认为Person(person1)相当于Person person1,而person1对象已声明
    Person person2 = person1;
	return 0;
}

 如果程序没有定义拷贝构造函数,c++会提供一个默认的拷贝构造函数,默认拷贝构造函数只能完成简单的赋值操作,无法完成堆内存成员数据的拷贝

#include <iostream>
using namespace std;
#include <iostream>
class Person {
private:
	string name;
	int age;
public:
	Person(string name,int age);
	~Person();
};
Person::Person(string name,int age) {
	this->name = name;
	this->age = age;
	cout << "调用person的构造函数:"<<name << endl;
}
Person::~Person() {
	cout << "调用析构函数"<<name<<" "<<age << endl;
}
int main() {
	Person person1("张三",18);
	Person person2(person1);
	return 0;
}

 (2)对象作为参数传递给函数。当函数的参数作为对象时,编译器会调用拷贝构造函数将实参传递给形参

void test1(Person p) {
	cout << "对象作为参数传递会调用拷贝构造函数,创建一个拷贝对象" << endl;
}
int main() {
	Person person1("张三",18);
	test1(person1);
	return 0;
}

引用传递则不会调用拷贝构造函数

void test1(Person &p) {
	cout << "对象作为引用就不会拷贝了,自然不会调用拷贝构造函数" << endl;
}
int main() {
	Person person1("张三", 18);
	test1(person1);
	return 0;
}

 (3)函数返回值为对象。当函数返回值为对象时,编译器会调用拷贝构造函数将返回值复制到临时对象传出

Person test1() {
	Person person("李四",18);
	cout << &person << endl;
	return person;
}
int main() {
	//Person person1("张三", 18);
	Person person1 = test1();
	cout << &person1 << endl;
	return 0;
}

 呃,这个应该和编译器有关,我这里并没有调用拷贝构造函数。老的版本或许有。

c++构造函数调用规则:

(1)如果自定义了有参构造函数,编译器不在提供默认构造函数,但是提供默认拷贝构造函数

#include <iostream>
using namespace std;
#include <iostream>
class Person {
public:
	string name;
	int age;
public:
	Person(string name, int age);
};
Person::Person(string name, int age) {
	this->name = name;
	this->age = age;
	cout << "调用构造函数:" << name << " " << age << endl;
}
int main() {
	//Person person;报错
	Person person1("张三", 18);
	Person person2(person1);//会调用拷贝构造函数
	cout << person2.name << " " << person2.age << endl;
	return 0;
}

 (2)如果自定义了拷贝构造函数,编译器不在提供其它默认构造函数

#include <iostream>
using namespace std;
#include <iostream>
class Person {
public:
	string name;
	int age;
public:
	Person(const Person& person);
};
Person::Person(const Person& person) {
	name = person.name;
	age = person.age;
	cout << "调用拷贝构造函数" << endl;
}
int main() {
	//Person person;报错
	return 0;
}

c++浅拷贝

如果类中有指针类型的数据,默认的拷贝构造函数只是进行简单的指针赋值,即将新对象的指针成员指向原有对象的指针指向的内存空间(即拷贝了地址),并没有为新对象的指针成员申请空间,这种情况称为浅拷贝。

注意:浅拷贝在析构指向堆内存空间的变量,往往会出现多次析构而导致程序错误。

#include <iostream>
using namespace std;
#include <iostream>
class Person {
public:
	string name;
	int *age;
public:
	Person(string name, int age);
	Person(const Person& person);
	~Person();
};
Person::Person(string name, int age) {
	this->name = name;
	this->age = new int(age);
	cout << "调用构造函数:" << endl;
}
Person::Person(const Person& person) {
	name = person.name;
	age = person.age;//编译器的简单赋值操作,即浅拷贝
	cout << "调用拷贝构造函数" << endl;
}
Person::~Person() {
	//析构代码将堆区开辟的内存数据释放
	if (age != NULL) {
		delete age;
		age = NULL;
	}
	cout << "调用析构函数:" <<name<< endl;
}
int main() {
	Person person1("张三", 18);
	Person person2(person1);
	return 0;
}

程序在此发生错误

Person::~Person() {
	//析构代码将堆区开辟的内存数据释放
	if (age != NULL) {
		delete age;
		age = NULL;
	}
	cout << "调用析构函数:" <<name<< endl;
}

 浅拷贝

 在析构person2对象时释放了age指向的堆内存空间的数据,当析构person1对象时age指向的堆内存空间已经被释放,再次释放内存空间的资源而引发了程序异常,称为重析构

c++深拷贝

c++深拷贝可以为新对象的指针分配一块内存空间,将数据复制到新的空间

#include <iostream>
using namespace std;
#include <iostream>
class Person {
public:
	string name;
	int* age;
public:
	Person(string name, int age);
	Person(const Person& person);
	~Person();
};
Person::Person(string name, int age) {
	this->name = name;
	this->age = new int(age);
	cout << "调用构造函数:" << name << endl;
}
Person::Person(const Person& person) {
	name = person.name;
	age = new int(*person.age);//深拷贝,为age指向新的堆区空间
	cout << "调用拷贝构造函数" << endl;
}
Person::~Person() {
	//析构代码将堆区开辟的内存数据释放
	if (age != NULL) {
		delete age;
		age = NULL;
	}
	cout << "调用析构函数:" << name << endl;
}
int main() {
	Person person1("张三", 18);
	Person person2(person1);
	cout << "person1.age地址" << person1.age << endl;
	cout << "person2.age地址" << person2.age << endl;
	cout << person2.name << " " << *person2.age << endl;
	return 0;
}

深拷贝

Person::Person(const Person& person) {
	name = person.name;
	age = new int(*person.age);//深拷贝,为age指向新的堆区空间
	cout << "调用拷贝构造函数" << endl;
}

注: 如果有堆区开辟的成员变量,需要自定义深拷贝构造函数,以防浅拷贝带来的析构各自对象资源的问题

c++含有成员对象的类的构造函数:

class B{
    ...
};
class A{
public:
    B b;//对象b作为类A的成员变量
};

如果类A包含一个类B对象作为成员变量,如果类B构造函数有参数,其参数要从类A的构造函数中传入,且必须以“:”运算符初始化类B对象。即若有构造函数时,实例化类A对象前先实例化类B对象。

#include <iostream>
using namespace std;
#include <iostream>
class Car {
private:
	string carName;
public:
	Car(string carName) :carName(carName) {
		cout << "Car类的构造函数:" << endl;
	};
};
class Person {
public:
	string name;
	Car car;
public:
	Person(string name,string carName):name(name),car(carName) {
		cout << "Person类构造函数:" << endl;
	}
};
int main() {
	Person person("张三", "大众汽车");
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

纯比例控制为什么会存在稳态误差,用纯增益系统举例

warning: 本文仅为个人思考&#xff0c;非常不严谨甚至可能会出现严重错误&#xff0c;请读者仔细甄别&#xff0c;若本文真的存在严重错误&#xff0c;恳请评论区纠正&#xff0c;我看到将会考虑修改或者删除文章 纯比例控制存在稳态误差是由其本质&#xff08;控制逻辑&#…

精炼计算机网络——物理层(一)

文章目录 前言2.1物理层的基本概念2.2 数据通信的基础知识2.2.1 数据通信系统的模型2.2.3 信道的极限容量 2.3 物理层下面的传输媒体2.3.1 导引型传输媒体2.3.2 非导引型传输媒体 总结 前言 经历了两篇文章的学习&#xff0c;相信读者们一定对计算机网络有了一个基础的了解。接…

一个新的ubuntu

1 安装cmake 方法一&#xff1a;现成的教程 ubuntu安装cmake_yuanzhoulvpi的博客-CSDN博客 方法二&#xff1a;自己总结的 安装openssl系统&#xff1a;sudo apt-get install libssl-dev 安装&#xff1a;sudo apt-get install build-essential 以上是防止安装cmake时缺少文…

PS磨皮插件portraiture最新版磨皮工具

Portraiture是一款智能磨皮插件&#xff0c;为Photoshop和Lightroom添加一键磨皮美化功能&#xff0c;快速对照片中皮肤、头发、眉毛等部位进行美化&#xff0c;无需手动调整&#xff0c;大大提高P图效率。全新4版本&#xff0c;升级AI算法&#xff0c;并独家支持多人及全身模式…

I2C工作流程

FM33A0XX的I2C接口只用作主机&#xff0c;且不支持多主机&#xff0c;因此挂在总线上的其他设备都是从机。总线上总是由主机提供同步时钟SCL&#xff0c;SDA数据流方向可以是主机发送从机接收&#xff0c;或者从机发送主机接收。 数据发送流程 1、主机发起 START 时序 2、主机…

C++之基础总结

目录 POD类型左值和右值静态全局变量(static)类型转换const/constexprconstconstexpr C中的关键字union基础知识点编译与函数参数入栈总结一些常见用法归纳&#xff1a; POD类型 平凡的和标准布局的——貌似和深度探索C对象模型中关于按位拷贝冲突 平凡的定义&#xff1a;符合…

Camtasia2023最好用的电脑屏幕录制软件

Camtasia2023是市场上最好的录像机和屏幕录制软件之一。强大的软件视频编辑程序的Camtasia 适用于Windows和iOS。 它支持多种流行的媒体格式&#xff0c;并对您创建的视频提供令人印象深刻的控制范围。3000多万专业人士在全球范围内使用Camtasia展示产品&#xff0c;教授课程&a…

文字的显示

文字的显示 文章目录 文字的显示1.文字编码方式2.英文和汉字的点阵显示3.显示中文“中”和“A”show_font.c结果 1.文字编码方式 数字>代表什么->显示为什么 GBK国标拓展 下列代码用不同编码方式保存utf-8.c ansi.c #include <stdio.h>int main(int argc ,char *…

网络编程之 Socket 套接字(使用数据报套接字和流套接字分别实现一个小程序(附源码))

文章目录 1. 什么是网络编程2. 网络编程中的基本概念1&#xff09;发送端和接收端2&#xff09;请求和响应3&#xff09;客户端和服务端4&#xff09;常见的客户端服务端模型 3. Socket 套接字1&#xff09;Socket 的分类2&#xff09;Java 数据报套接字通信模型3&#xff09;J…

基于Open3D的点云处理2-Open3D的IO与数据转换

三维数据类型 点云 某个坐标系下的点数据集&#xff0c;每个点包括三维坐标X&#xff0c;Y&#xff0c;Z、颜色、分类值、强度值、时间等信息&#xff1b; 储存格式&#xff1a;pts、LAS、PCD、xyz、asc、ply等&#xff1b;Mesh 多边形网格&#xff0c;常见的是三角网格&#…

有研究员公开了一个解析并提取 Dell PFS BIOS 固件的工具(上)

导语&#xff1a;研究员公开了一个解析并提取 Dell PFS BIOS 固件的工具。 Dell PFS BIOS提取器 介绍 解析 Dell PFS BIOS 映像并提取其 SPI/BIOS/UEFI 固件组件。它支持所有Dell PFS 修订版和格式&#xff0c;包括最初在 ThinOS 包中LZMA压缩、ZLIB压缩或拆分成块的格式。输出…

Kafka上的优化经验

1. 平滑扩容 Motivation kafka扩容⼀台新机器的流程 假如集群有 3 个 broker &#xff0c;⼀共有 4 个 TP &#xff0c;每个 3 副本&#xff0c;均匀分布。现在要扩容⼀台机器&#xff0c; 新 broker 加⼊集群后需要通过⼯具进⾏ TP 的迁移。⼀共迁移 3 个 TP 的副…

Spring Boot集成ShardingSphere实现按月数据分片及创建自定义分片算法 | Spring Cloud 44

一、前言 在前面我们通过以下章节对数据分片有了基础的了解&#xff1a; Spring Boot集成ShardingSphere实现数据分片&#xff08;一&#xff09; | Spring Cloud 40 Spring Boot集成ShardingSphere实现数据分片&#xff08;二&#xff09; | Spring Cloud 41 Spring Boot集…

权限提升:信息收集 .(Linux系统)

权限提升&#xff1a;信息收集. 权限提升简称提权&#xff0c;由于操作系统都是多用户操作系统&#xff0c;用户之间都有权限控制&#xff0c;比如通过 Web 漏洞拿到的是 Web 进程的权限&#xff0c;往往 Web 服务都是以一个权限很低的账号启动的&#xff0c;因此通过 Webshel…

1.1 基于B/S 结构的 Web 应用

文章目录 1.1 基于B/S 结构的 Web 应用1.2 JDK安装与配置1.3 服务器Tomcat下载与安装1.4 Eclipse安装与使用1.4.1 Eclipse 下载及创建Dynamic Web Project1.4.2 Eclipse 中的编码问题1.4.3 将Tomcat和Eclipse相关联1.4.4 Eclipse 自动部署项目到 Tomcat 的 webapps 目录 1.5 My…

【AWS入门】AWS Lamda

目录 创建一个Lamda函数用Lamda函数控制启停EC2实例创建一台EC2实例创建角色创建lamda函数 使用Amazon EventBridge计划启停实例创建EventBridge 用户往S3存储桶上传图片文件&#xff0c;触发Lambda函数&#xff0c;将图片压缩并上传至另一个存储桶创建两个存储桶通过Cloudform…

【SpringMVC】| SpringMVC执行流程原理 | 常用注解 剥析

MVC目录 一. &#x1f981; MVC模型二. &#x1f981; SpringMVC1. SpringMVC执行流程&#xff08;重点&#xff09;Ⅰ. SpringMVC四大组件Ⅱ. 执行流程 2. RequestMapping3. RequestParam4. ReuqestHeader & CookieValue5. RESTful风格支持Ⅰ. 传统 vs restfulⅡ. PathVar…

【网络技术】什么是CNI

序言 你只管努力&#xff0c;其他交给时间&#xff0c;时间会证明一切。 Never look back unless you are planning to go that way. 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用…

【应急响应】日志自动提取分析项目ELKLogkitLogonTracerAnolog等

日志自动提取-七牛Logkit&观星应急工具 1、七牛Logkit&#xff1a;(Windows&Linux&Mac等) https://github.com/qiniu/logkit/ 支持的数据源&#xff08;各类日志&#xff0c;各个系统&#xff0c;各个应用等&#xff09; File: 读取文件中的日志数据&#xff0c;包…

面了一个4年经验的测试工程师,自动化都不会也要15k,我也是醉了····

在深圳这家金融公司也待了几年&#xff0c;被别人面试过也面试过别人&#xff0c;大大小小的事情也见识不少&#xff0c;今天又是团面的一天&#xff0c; 一百多个人都聚集在一起&#xff0c;因为公司最近在谈项目出来面试就2个人&#xff0c;无奈又被叫到面试房间。 整个过程…