C++零碎记录(三)

news/2024/12/2 22:37:44/

作者:小王同学在积累
链接:https://www.zhihu.com/question/437657370/answer/1692846096
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

5. 构造函数和析构函数

5.1 构造函数和析构函数的作用

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

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

5.2 构造函数和析构函数的语法

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

1. 构造函数,没有返回值也不写void。
2. 函数名称与类名相同。
3. 构造函数可以有参数,因此可以重载。
4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次。

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

1. 析构函数,,没有返回值也不写void。
2. 函数名称与类名相同,在名称前加上符号。
3. 析构函数不可以有参数,因此不可以发生重载。
4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次。
#include <iostream>
using namespace std;
#include <string>//对象的初始化和清理
//1、构造函数 进行初始化操作class Person
{
public:  //无论是构造函数还是析构函数都是在public作用域下//1.1、构造函数//没有返回值 不用写void//函数名 与类名相同//构造函数可以有参数,可以发生重载//创建对象的时候,构造函数会自动调用,而且只调用一次Person(){cout << "Person 构造函数的调用" << endl;}/*如果你不写,编译器会自动创建一个,但是里面是空语句Person(){}*///1. 析构函数,,没有返回值也不写void。//2. 函数名称与类名相同,在名称前加上符号。//3. 析构函数不可以有参数,因此不可以发生重载。//4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次。~Person(){cout << "Person 析构函数的调用" << endl;}
};//构造和析构都是必须有的实现,如果我们自己不提供,编译器会提供一个空实现
void test01()
{Person p;  //创建对象的时候,自动调用构造函数//这个对象p是一个局部变量,是在栈上的数据,test01执行完,释放这个对象
}int main()
{//方式一:test01();   // 析构释放时机在test01运行完前,test01函数运行完后,里面的对象就被释放了/*方式二:     //创建对象的时候,自动调用构造函数Person p;   //只有构造函数,没有析构函数,只有main函数结束完前,对象要释放掉了,才会调用析构函数*/system("pause");return 0;}

运行结果:

  • Person 构造函数的调用
  • Person 析构函数的调用
  • 请按任意键继续. . .

5.3 构造函数的分类及调用

① 两种分类方式:

1. 按参数分为:有参构造和无参构造。
2. 按类型分为:普通构造和拷贝构造。

② 三种调用方式:

1. 括号法
2. 显示法
3. 隐式转换法
#include <iostream>
using namespace std;//1构造函数的分类及调用
//分类
//按照参数分类:无参构造(默认构造) 和 有参构造
class Person
{
public: //构造函数  编译器默认的构造函数是无参的Person(){cout << "Person 无参构造函数的调用" << endl;}Person(int a){age = a;cout << "Person 有参构造函数的调用" << endl;}//拷贝构造函数Person( const Person &p)  //用引用的方式传进来,不能改变原来的对象的属性,所以用const        {// 将传入的人人身上的所有属性,拷贝到我身上cout << "Person 拷贝构造函数的调用" << endl;age = p.age;}~Person(){cout << "Person 析构函数的调用" << endl;}int age;
};//调用
void test01()
{/*//1、括号法Person p1;       //默认构造函数调用Person p2(10);   //有参构造函数Person p3(p2);   //拷贝构造函数cout << "p2的年龄为:" << p2.age << endl;cout << "p3的年龄为:" << p3.age << endl;//注意事项1//调用默认构造函数的时候,不要加()。//下面这行代码,编译器会认为是一个函数的声明,像void func(),不会认为在创建对象。//Person p1();*//** //2、显示法Person p1;  //创建一个对象,这个对象调用的是无参构造Person p2 = Person(10); //有参构造  将匿名对象起了一个名称p2Person p3 = Person(p2); //创建一个对象,这个对象调用的是拷贝构造Person(10); //匿名对象 特点:当前行执行结束后,系统会立即回收匿名对象cout << "aaaa" << endl;  //通过打印时机可以得到:test还没结束,就运行析构函数了//注意事项2//不要利用拷贝构造函数 初始化匿名对象  编译器认为 Person(p3) 等价于 Person p3,//编译器会认为这是一个对象的声明,而上面已经有一个p3了,Person p3 = Person(p2);因此编译器认为重定义了       Person(p3);*///3、隐式转换法Person p4 = 10; //相当于 写了 Person p4 = Person(10);  调用有参构造Person p5 = p4; //调用拷贝构造}
int main()
{test01();system("pause");return 0;}

运行结果:

  • Person 有参构造函数的调用
  • Person 拷贝构造函数的调用
  • Person 析构函数的调用
  • Person 析构函数的调用
  • 请按任意键继续. . .

5.4 拷贝构造函数调用时机

① C++中拷贝构造函数调用时机通常有三种情况。

1. 使用一个已经创建完毕的对象来初始化一个新对象。
2. 值传递的方式给函数参数传值。
3. 以值方式返回局部对象。
#include <iostream>
using namespace std;//拷贝构造函数调用时机//1、使用一个已经创建完毕的对象来初始化一个新对象//2、值传递的方式给函数参数传值//3、值方式返回局部对象class Person
{
public:Person(){cout << "Person 默认构造函数调用" << endl;}Person(int age){m_Age = age;cout << "Person 有参构造函数调用" << endl;}Person(const Person& p){m_Age = p.m_Age;cout << "Person 拷贝构造函数调用" << endl;}~Person(){cout << "Person 析构函数调用" << endl;}int m_Age;};//1、使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{Person p1(20);Person p2(p1);cout << "p2的年龄为:" << p2.m_Age << endl;
}//2、值传递的方式给函数参数传值
void doWork(Person p)
{}void test02()
{Person p;doWork(p);   //实参传给形参的时候,会调用拷贝构造函数,这个是值传递,是一个临时的副本//拷贝出去的p和原来的p 不是一个p
}//3、值方式返回局部对象
Person doWork2() //返回值类型为Person对象
{Person p1;   //局部对象cout << (int*)&p1 << endl;return p1;  //以值的方式返回一个拷贝的对象给外部,拷贝出一个对象p1'与原对象p1不一样,调用拷贝构造函数//程序运行结束,释放原p1,调用析构函数
}void test03()
{Person p = doWork2(); //这里没有调用拷贝构造函数,直接用p接收拷贝对象p1’cout << (int*)&p << endl;//程序运行结束,释放拷贝的对象p1',调用析构函数
}int main()
{//test01();	//test02();test03();system("pause");return 0;}

运行结果:

  • Person 默认构造函数调用
  • 005DF904
  • Person 拷贝构造函数调用
  • Person 析构函数调用
  • 005DF9FC
  • Person 析构函数调用
  • 请按任意键继续. . .

5.5 构造函数调用规则

5.5.1 构造函数调用规则

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

1. 默认构造函数(无参,函数体为空)
2. 默认析构函数(无参,函数体为空)
3. 默认拷贝构造函数,对属性进行值拷贝

② 构造函数调用规则如下:

1. 如果用户定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造。
2. 如果用户定义拷贝函数,C++不会再提供其他构造函数。

③ 巧记法,如下图所示,如果定义中间的,上面的就默认不定义了,下面的默认定义。

5.5.2 调用定义的拷贝构造函数

#include <iostream>
using namespace std;//构造函数的调用规则
//1、创建一个类,C+=编译器会给每个类都添加至少3个函数
//默认构造 (空实现)
//析构函数 (空实现)
//拷贝构造 (值拷贝)class Person
{
public:Person(){cout << "Person 默认构造函数调用" << endl;}Person(int age){m_Age = age;cout << "Person 有参构造函数调用" << endl;}Person(const Person & p){m_Age = p.m_Age;cout << "Person 拷贝构造函数调用" << endl;}~Person(){cout << "Person 析构函数调用" << endl;}int m_Age;};void test01()
{Person p;p.m_Age = 18;Person p2(p);cout << "p2的年龄:" << p2.m_Age << endl;
}int main()
{test01();system("pause");return 0;}

运行结果:

  • Person 默认构造函数调用
  • Person 拷贝构造函数调用
  • p2的年龄:18
  • Person 析构函数调用
  • Person 析构函数调用
  • 请按任意键继续. . .

5.5.3 调用默认的拷贝构造函数

#include <iostream>
using namespace std;//构造函数的调用规则
//1、创建一个类,C+=编译器会给每个类都添加至少3个函数
//默认构造 (空实现)
//析构函数 (空实现)
//拷贝构造 (值拷贝)class Person
{
public:Person(){cout << "Person 默认构造函数调用" << endl;}Person(int age){m_Age = age;cout << "Person 有参构造函数调用" << endl;}//编译器自动提高拷贝构造函数~Person(){cout << "Person 析构函数调用" << endl;}int m_Age;};void test01()
{Person p;p.m_Age = 18;Person p2(p);  //调用编译器默认的拷贝构造函数会把p的所有属性拷贝过来cout << "p2的年龄:" << p2.m_Age << endl;
}int main()
{test01();system("pause");return 0;}

运行结果:

  • Person 默认构造函数调用
  • p2的年龄:18
  • Person 析构函数调用
  • Person 析构函数调用
  • 请按任意键继续. . .

5.5.4 调用定义的有参构造函数

#include <iostream>
using namespace std;//构造函数的调用规则
//1、创建一个类,C+=编译器会给每个类都添加至少3个函数
//默认构造 (空实现)
//析构函数 (空实现)
//拷贝构造 (值拷贝)class Person
{
public:Person(int age){m_Age = age;cout << "Person 有参构造函数调用" << endl;}//编译器自动提高拷贝构造函数~Person(){cout << "Person 析构函数调用" << endl;}int m_Age;};void test02()
{Person p; //如果写了有参构造函数,编译器就不再提供默认构造,依然提供拷贝构造构造//由于没有默认构造函数,所以报错Person p2(p);
}int main()
{test02();system("pause");return 0;}

5.5.5 调用定义的拷贝构造函数

#include <iostream>
using namespace std;//构造函数的调用规则
//1、创建一个类,C+=编译器会给每个类都添加至少3个函数
//默认构造 (空实现)
//析构函数 (空实现)
//拷贝构造 (值拷贝)class Person
{
public://如果写了拷贝构造函数,编译器就不再提供其他普通构造函数Person(const Person& p){m_Age = p.m_Age;cout << "Person 拷贝构造函数调用" << endl;}~Person(){cout << "Person 析构函数调用" << endl;}int m_Age;};void test01()
{Person p;    //没有默认构造函数,报错Person(10);  //没有有参构造函数,报错Person p2(p);cout << "p2的年龄:" << p2.m_Age << endl;
}int main()
{test01();system("pause");return 0;}

http://www.ppmy.cn/news/1091743.html

相关文章

RC-u4 相对论大师(bfs求解指定路径)

PTA | 程序设计类实验辅助教学平台 题解&#xff1a; bfs可以求解从根节点到叶子节点的指定路径&#xff0c;这里的vis[]不是为了防止访问到父节点&#xff0c;更多的是为了缩小路径长度&#xff0c;mpp和mp的映射也很巧妙&#xff0c;开始我用的还是map<pair<string,s…

【无标题】jmeter-正则表达式提取测试结果数据存储到CSV表

正则表达式提取数据存储到CSV表格中 String filePath"D://DATA.csv"; File filenew File(filePath); file.createNewFile(); FileWriter write new FileWriter(file,true); write.write("${DATA}""\n"); write.flush(); write.close();

Java字符串查找

目录 1.查找字符 &#xff08;1&#xff09;以索引查找字符 &#xff08;2&#xff09;以字符查找索引 2.查找字符串 在给定的字符串中查找需要的字符或字符串是常见的操作&#xff0c;以下是String类中常用的查找方法。 1.查找字符 查找字符分为两种情况&#xff1a;一种…

【校招VIP】前端算法考点之快慢指针题型

考点介绍&#xff1a; 链表是校招面试里手撕代码出现频度比较高的题型&#xff0c;三线和中小厂会考察简单的链表反转&#xff0c;大厂会进一步考察复杂度和双指针问题&#xff0c;比如中间元素、是否存在环等。 『前端算法考点之快慢指针题型』相关题目及解析内容可点击文章末…

利用微调的deberta-v3-large来预测情感分类

前言&#xff1a; 昨天我们讲述了怎么利用emotion数据集进行deberta-v3-large大模型的微调&#xff0c;那今天我们就来输入一些数据来测试一下&#xff0c;看看模型的准确率&#xff0c;为了方便起见&#xff0c;我直接用测试集的前十条数据 代码&#xff1a; from transfor…

用半天时间从零开始复习前端之html

目录 前言 科班生的标配&#xff1a;半天听完一门标记型语言 准备工作 webstorm2022 webstrom 第一个html页面 body h系列标签 行标签和块标签 列表标签 表格标签&#xff08;另起一篇&#xff09; 万能的input 1.快速生成多个标签 2.同时选中多个 前言 科班生的标…

AI:06-基于OpenCV的二维码识别技术的研究

二维码作为一种广泛应用于信息传递和识别的技术,具有识别速度快、容错率高等优点。本文探讨如何利用OpenCV库实现二维码的快速、准确识别,通过多处代码实例展示技术深度。 二维码作为一种矩阵型的条码,广泛应用于各个领域,如商品追溯、移动支付、活动签到等。二维码的快速…

LeetCode 133. Clone Graph【图,DFS,BFS,哈希表】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…