浅谈C++|构造.析构函数篇

2023-09-16 22:37:46


一对象的初始化和处理

1.1构造函数和析构函数

C++拥有构造函数和析构函数,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器提供的构造函数和析构函数是空实现。

·构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

·析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。

语法:

构造函数语法:类名(){}

  1.构造函数,没有返回值也不写void

  2.函数名称与类名相同

  3.构造函数可以有参数,因此可以发生重载

  4.程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

析构函数语法:~类名(){}

  1.析构函数,没有返回值也不写void

  2.函数名称与类名相同,在称前加上符号

  3.析构函数不可以有参数,因此不可以发生重载

  4.程序在对象销毁前会自动调用析构,

代码: 

#include <iostream>
using namespace std;
class person {
public:
	person() {
		cout << "person 构造函数调用" << endl;
	}
	~person() {
		cout << "person 析构函数调用" << endl;
	}

};
void func() {
	person a;
}
int main() {
	func();
	return 0;
}

 1.2构造函数的分类和调用

两种分类方式:

  按参数分为:有参构造和无参构造

  按类型分为:普通构造和拷贝构造

三种调用方式:

  括号法

  显示法

  隐式转换发

1.2.1构造函数分类

(1)有参和无参构造函数

代码:

#include <iostream>
using namespace std;
class person {
public:
	person() {
		cout << "person 无参构造函数调用" << endl;
	}
	person(int a) {
		cout << "person 有参构造函数调用" << endl;
	}
	~person() {
		cout << "person 析构函数调用" << endl;
	}

};
void func() {
	person a(1);
}
int main() {
	func();
	return 0;
}

(2)普通和拷贝构造函数 

 代码:

#include <iostream>
using namespace std;
class person {
public:
	int age;
	person() {
		cout << "person的普通构造函数" << endl;
	}
	person(const person& p) {
		age = p.age;
		//可以将传入的人的所有属性,拷贝到神上
		cout << "person的拷贝构造函数" << endl;
	}
};
void func() {
	//1.括号法
	person p1;
	p1.age = 10;
	person p2(p1);
	cout << p2.age << endl;
}
int main() {
	func();
	return 0;
}

1.2.2构造函数的调用方式

括号法

显示法

隐式转换发

(1)括号法:

#include <iostream>
using namespace std;
class person {
public:
	int age;
	person() {
		cout << "person的普通构造函数" << endl;
	}
	person(int a) {
		age = a;
		cout << "person的普通构造函数" << endl;
	}
	person(const person& p) {
		age = p.age;
		//可以将传入的人的所有属性,拷贝到神上
		cout << "person的拷贝构造函数" << endl;
	}
};
void func() {
	//1.括号法
	person p1;       //默认
	p1.age = 10;
	person p2(20);   //有参
	person p3(p1);   //拷贝

	//2.
}
int main() {
	func();
	return 0;
}

注意: person p() 会被认为是函数声明,并不是调用默认构造

(2)显示法:

#include <iostream>
using namespace std;
class person {
public:
	int age;
	person() {
		cout << "person的普通构造函数" << endl;
	}
	person(int a) {
		age = a;
		cout << "person的普通构造函数" << endl;
	}
	person(const person& p) {
		age = p.age;
		//可以将传入的人的所有属性,拷贝到神上
		cout << "person的拷贝构造函数" << endl;
	}
};
void func() {
	//2.显示法
	person p1;       //默认
	p1.age = 10;
	person p2=person(20);   //有参
	person p3=person(p1);   //拷贝

	//2.
}
int main() {
	func();
	return 0;
}

1.person(20)称为匿名对象,当前行执行后系统会立即回收匿名对象

2.不要用拷贝构造函数初始化匿名对象,person(p3) ->person  p3;相当于重新定义一个p3,发生重定义错误。

(3) 隐式转换法:

#include <iostream>
using namespace std;
class person {
public:
	int age;
	person() {
		cout << "person的普通构造函数" << endl;
	}
	person(int a) {
		age = a;
		cout << "person的普通构造函数" << endl;
	}
	person(const person& p) {
		age = p.age;
		//可以将传入的人的所有属性,拷贝到神上
		cout << "person的拷贝构造函数" << endl;
	}
};
void func() {
	//3.隐式转换发
	person p1;       //默认
	person p2=10;    //相当于person p2=person(10);
	person p3 = p2;  //相当于person p3 = person(p2);
}
int main() {
	func();
	return 0;
}

1.3拷贝构造函数调用时机

1.使用一个已经创建完毕的对象来初始化一个新对象。

2.值传递的方式给函数参数传值

3.以值方式返回局部对象

 1.3.1旧对象创建新对象

代码:

#include <iostream>
using namespace std;
class person {
public:
	int a;
	int b;
	person() {
		cout << "person的无参构造函数" << endl;
	}
	person(int a1) {
		a = a1;
		cout << "person的有参构造函数" << endl;
	}
	person(const person &p) {
		a = p.a;
		cout << "person的拷贝构造函数" << endl;
	}
	~person() {
		cout << "person的析构函数" << endl;
	}
};
void fun() {
	person A(10);
	person B(A);
	cout << B.a << ' ' << B.b << endl;
}
int main() {
	fun();
	return 0;
}

 1.3.2值传递的方式给函数参数传值

作为函数值传递时,只会复制拷贝函数中已将定义要拷贝的变量,其余的即使已经外部定义了,只要拷贝函数中没有指明要拷贝,形参就不会拷贝。

#include <iostream>
using namespace std;
class person {
public:
	int a;
	int b;
	int c;
	person() {
		cout << "person的无参构造函数" << endl;
	}
	person(int a1) {
		a = a1;
		cout << "person的有参构造函数" << endl;
	}
	person(const person &p) {
		a = p.a;
		cout << "person的拷贝构造函数" << endl;
	}
	~person() {
		cout << "person的析构函数" << endl;
	}
};
void fun(person A) {
	cout << A.a << ' ' << A.b <<' '<<A.c << endl;
}
int main() {
	person A(10);
	A.b = 90;
	cout << A.a << ' ' << A.b << ' ' << A.c << endl;
	fun(A);
	return 0;
}

1.3.3以值方式返回局部对象

同做做参数一样,也只复制拷贝函数中定义要复制的部分,并不是完全复制。

#include <iostream>
using namespace std;
class person {
public:
	int a;
	int b;
	int c;
	person() {
		cout << "person的无参构造函数" << endl;
	}
	person(int a1) {
		a = a1;
		cout << "person的有参构造函数" << endl;
	}
	person(const person &p) {
		a = p.a;
		cout << "person的拷贝构造函数" << endl;
	}
	~person() {
		cout << "person的析构函数" << endl;
	}
};
person fun() {
	person A(10);
	A.b = 90;//已经赋值,但是形参还是随机值
	cout << A.a << ' ' << A.b << ' ' << A.c << endl;
	return A;
	
}
int main() {
	person A=fun();
	cout << A.a << ' ' << A.b << ' ' << A.c << endl;
	return 0;
}

 1.4构造函数调用规则

默认情况下,c++编译器至少给一个类添加3个函数

1.默认构造函数(无参,函数体为空)

2.默认析构函数(无参,函数体为空)

3.默认拷贝构造函数,对属性进行值拷贝(全部)

构造函数调用规则如下:

1.如果用户定义有参构造函数,c+不在提供默认无参构造,但是会提供默认拷贝构造

2.如果用户定义拷贝构造函数,C++不会再提供其他构造函数

代码:

#include <iostream>
using namespace std;
class person {
public:
	int a;
	int b;
	int c;
	
	person(int a1) {
		a = a1;
		cout << "person的有参构造函数" << endl;
	}
	
	~person() {
		cout << "person的析构函数" << endl;
	}
};
person fun() {
	person A(10);
	A.b = 90;
	//person B;   报错
	return A;
	
}
int main() {
	fun();
	return 0;
}

1.5深拷贝和浅拷贝

浅拷贝就是简单的赋值操作,深拷贝就是

代码: 

#include <iostream>
using namespace std;
class person {
public:
	int a;
	int* b;
	person(int a1, int b2) {
		a = a1;
		b = new int(b2);
	}
	person(const person &p) {  //浅拷贝
		a = p.a;
		b = p.b;
	}
	~person() {  //浅拷贝
		if (b != NULL) {
			delete b;
			b = NULL;
		}
	}
};
void fun() {
	person a(10,90);
	person b(a);
	cout << b.a << ' ' << *b.b << endl;
}
int main() {
	fun();
	return 0;
}

 此时代码会报错,因为此时有两个析构函数,因此会是否两次相同地址的空间,此时要改为深拷贝。

代码: 

#include <iostream>
using namespace std;
class person {
public:
	int a;
	int* b;
	person(int a1, int b2) {
		a = a1;
		b = new int(b2);
	}
	person(const person &p) {  //深拷贝
		a = p.a;
		b = new int(*(p.b));
	}
	~person() {  
		if (b != NULL) {
			delete b;
			b = NULL;
		}
	}
};
void fun() {
	person a(10,90);
	person b(a);
	cout << b.a << ' ' << *b.b << endl;
}
int main() {
	fun();
	return 0;
}

 

  1.6初始化列表

C++提供了初始化列表语法,用来初始化属性。

 语法:构造函数()  :  属性值1(值1),属性值2(值2)....... { }

代码:

#include <iostream>
using namespace std;
class person {
public:
	int a;
	int b;
	int c;
	person(int a1,int b1,int c1) :a(a1), b(b1), c(c1) {  //初始化列表

	}
};
void fun() {
	person p(30, 3,78);
	cout << p.a << ' ' << p.b << ' ' << p.c << endl;
}
int main() {
	fun();
	return 0;
}

更多推荐

软件设计中常见的设计模式

以下是常见的设计模式,并且给出了应用场景:工厂模式(FactoryPattern):用于创建对象,隐藏了具体对象的创建细节,客户端只需要通过工厂接口获取对象即可。应用场景包括:当需要根据不同的参数生成不同类型的对象时;当需要遵循“开闭原则”,即增加新类型时,只需要添加新的工厂类。单例模式(SingletonPatter

Kafka 常见问题

文章目录kafka如何确保消息的可靠性传输Kafka高性能的体现利用Partition实现并行处理利用PageCache如何提高Kafka性能调整内核参数来优化IO性能减少网络开销批处理数据压缩降低网络负载高效的序列化方式kafka如何确保消息的可靠性传输消费端弄丢了数据唯一可能导致消费者弄丢数据的情况,就是消费到了这

java中零拷贝和深拷贝的原理以及实现探究

深拷贝和零拷贝是两个在Java中广泛使用的概念,它们分别用于对象复制和数据传输优化。下面将详细介绍这两个概念的原理,并给出相应的Java代码示例。深拷贝深拷贝(DeepCopy)原理:深拷贝是创建一个对象的完全独立副本,包括对象本身、引用类型的属性和子对象。可以通过序列化和反序列化来实现深拷贝。首先,需要确保要拷贝的对

【2023,学点儿新Java-47】常见字符集介绍:ASCII码、 ISO-8859-1字符集、GBxxx字符集、Unicode码的缺陷、UTF-8 | 补充:条件运算符的练习

前情提要:【2023,学点儿新Java-46】条件运算符:语法格式及示例;基础练习:获取两个数/三个数中的较大值;星期运算|附:测试代码位运算符的使用|运算符优先级【2023,学点儿新Java-45】位运算符:基本语法(左移<<、右移>>、无符号右移>>>、按位与&、按位或|、按位异或^、按位取反~)|补充练习:逻辑运

redis实战-redis实现异步秒杀优化

秒杀优化-异步秒杀思路未优化的思路当用户发起请求,此时会请求nginx,nginx会访问到tomcat,而tomcat中的程序,会进行串行操作,分成如下几个步骤1、查询优惠卷2、判断秒杀库存是否足够3、查询订单4、校验是否是一人一单5、扣减库存6、创建订单在这六步操作中,又有很多操作是要去操作数据库的,而且还是一个线程

android-适配方案-密度适配-最小宽度限定符

一最小宽度的该如何计算始终以真实屏幕的最小宽度作为匹配计算,无论横竖屏转变都是以最小宽度为准匹配实际适配调试经验:因为适配过程中可能会动态的调节设备像素和密度。根据如下工具代码打日志,可以快速调试当前设备。packagecom.jicaai.radio.utils;importandroid.app.Activity;

Golang开发--计时器(Timer)和定时器(Ticker)

计时器(Timer)在Go中,可以使用time包提供的计时器(Timer)来执行定时任务。计时器允许你在指定的时间间隔后执行某个操作。time.Timer结构表示一个计时器,它会在指定的时间段后触发单次操作。创建计时器:使用time.NewTimer(duration)函数创建一个计时器,其中duration是一个ti

9月13-14日上课内容 第三章 ELK日志分析系统及部署实例

本章结构ELK日志分析系统简介ELK日志分析系统分为ElasticsearchLogstashKibana日志处理步骤1.将日志进行集中化管理2.将日志格式化(Logstash)并输出到Elasticsearch3.对格式化后的数据进行索引和存储(Elasticsearch)4.前端数据的展示(Kibana)Elast

python自(2)切片 字典 遍历删除添加修改查询定义函数函数返回值作用域序列化异常报错urllib使用一个类型六个方法下载 视频音频图片

切片##切片#s='helloword'##下标索引为0的#print(s[0])#h##左闭右开(左是下标开始的,右是几个索引值)例如从0开始算4个索引值#print(s[0:4])#hell##更改起始值的开始位置#print(s[1:])#elloword##下标结束位置#print(s[:5])#hello##

二叉搜索树(BST,Binary Search Tree)

文章目录1.二叉搜索树1.1二叉搜索树概念1.2二叉搜索树的查找1.3二叉搜索树的插入1.4二叉搜索树的删除2二叉搜索树的实现3二叉搜索树的应用3.1二叉搜索树的性能分析1.二叉搜索树1.1二叉搜索树概念二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:若它的左子树不为空,则左子树上所有节点的值都

面试官:用Vue3.0 写过组件吗?如果想实现一个 Modal你会怎么设计?

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录一、组件设计二、需求分析三、实现流程目录结构组件内容实现API形式事件处理其他完善一、组件设计组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式现在有一个场景,点击新增与编辑都弹框出

热文推荐