C++入门之类和对象

C++入门之类和对象

文章目录

  • C++入门之类和对象
    • 1. 类的6个默认对象
    • 2. 构造函数
      • 2.1 概念
      • 2.2 特性
      • 2.3 补丁
    • 3. 析构函数
      • 3.1 概念
      • 3.2 特性
      • 3.3 总结
    • 4. 拷贝构造函数
      • 4.1 概念
      • 4.2 特性
      • 4.3 总结

1. 类的6个默认对象

如果一个类中什么都没有,那么这个类就是一个空类。但是,任何类如果什么都不写的话,编译器会自动生成6个默认成员函数

默认成员函数:用户没有显式实现(用户没有写),编译器自动生成的成员函数被称为默认成员函数

class Data{};

在这里插入图片描述

2. 构造函数

2.1 概念

假设有以下类:

#include <iostream>
using namespace std;
class Date
{
public:void Init(int year = 2024, int month = 4, int day = 15){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Init();d1.Print();Date d2;d2.Init(2024, 5, 1);d2.Print();return 0;
}

上述类中是使用Init函数对类进行初始化,Init使用全缺省参数,如果没有传值的话,使用缺省值初始化,但是对于这这种类,就算不初始化也不会有什么问题,但是对于顺序表,链表等,如果不初始化就会报错,往往我们会容易忘记调用初始化,这时候,构造函数就派上用场了

构造函数是一种特殊的函数,名字与类名相同,没有返回值(在默认成员函数中,没有返回值指的都是不写),在创建类对象时由编译器自动调用,保证每个成员都有一个初始值,并且在类对象整个生命周期只会调用一次

2.2 特性

构造函数是一种特殊的函数,构造函数并不是用于开辟空间创建对象,而是为对象进行初始化

特征

  1. 函数名与类名相同
  2. 函数没有返回值(不写返回值)
  3. 对象实例化时编译器会自动调用
  4. 构造函数可以重载(可以根据需求写多个初始化方式)
  5. 如果类中没有显式定义构造函数(没有写),编译器就会自动生成一个无参的构造函数,反之,编译器则不会生成
  6. 由编译器生成的无参构造函数,不会对类中的内置类型(int char等等)进行处理,但是对调用类中自定类型(class struct union等等)的构造函数,如果类中自定类型还是没有则也不处理
    C++中没有规定对自定类型(class struct union等等)初始化成0或者其他,取决于编译器的实现
  7. 无参构造函数全缺省的构造函数由编译器自动生成构造函数都可以被称为默认参构造函数,但是默认参构造函数只能存在一个

示例1:

#include <iostream>
using namespace std;
class Date
{
public://Date(int year, int month, int day) (这种写法会报错,这种不是默认的构造函数)Date(int year = 2024, int month = 4, int day = 15){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;//调用全缺省的构造函数(2024-4-15)d1.Print();return 0;
}

示例2:

#include <iostream>
using namespace std;
class Time
{
private:int _hour;int _minute;int _second;
};
class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;Time a;
};int main()
{Date d1;d1.Print();return 0;
}

在这里插入图片描述

示例3:

#include <iostream>
using namespace std;
class Date
{
public:Date(){}Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Print();return 0;
}

存在多个构造函数,报错

2.3 补丁

由于不对内置类型进行初始化,所以在C++ 11中,打了一个补丁,允许内置成员在声明时可以给一个默认值

#include <iostream>
using namespace std;
class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year = 2024;int _month = 4;int _day = 15;
};int main()
{Date d1;d1.Print();return 0;
}

用声明时的默认值初始化(2024-4-15)

总结:
一般情况下,构造函数都要由我们自己实现,少部分情况下可以不用实现(如果类中只有自定类型,而这个自定类型内部存在构造函数),例如:MyQueue

3. 析构函数

3.1 概念

析构函数是与构造函数相反的一种特殊函数,析构函数不是对对象进行销毁,局部变量的销毁是由编译器处理的,而是析构函数是对对象中资源的清理,且会在对象销毁时自动调用

3.2 特性

  1. 在类名前面加上~
  2. 无参数无返回值(不写返回值)
  3. 一个类只有一个析构函数,如果没有显式定义(没有写),则编译器会自动生成默认析构函数(由于没有参数,析构函数不能重载)
  4. 在对象生命周期结束时,编译器会自动调用析构函数
  5. 与构造函数相似的是,析构函数不会对内置类型进行处理,对自定类型则是调用其析构函数

示例:

#include <iostream>
using namespace std;
class Stack
{
public:Stack(int n = 4){cout << "Stack()" << endl;int* tmp = (int*)malloc(sizeof(int) * n);if (nullptr == tmp){perror("malloc fail");return;}_arr = tmp;_capacity = n;_size = 0;}void Push(int x){//扩容_arr[_size] = x;_size++;}~Stack(){cout << "~Stack()" << endl; //方便查看if (_arr) //防止被多次销毁,加个判断{free(_arr);_arr = nullptr;_capacity = 0;_size = 0;}}
private:int* _arr;int _size;int _capacity;
};
int main()
{Stack s;s.Push(1);s.Push(2);s.Push(3);s.~Stack();return 0;
}

在这里插入图片描述

析构函数也是可以显式调用的

3.3 总结

  1. 在没有需要资源清理的时候可以不写析构函数,
    a. 如Date类,没有需要清理的内置类型
    b.没有需要清理的内置类型,剩下的其他自定类型中存在析构函数,如MyQueue,也不需要写析构函数
    2.有资源清理就要写析构函数,如Stack,List

4. 拷贝构造函数

4.1 概念

拷贝构造函数:只有一个形参,该形参为本类型对象的引用(一般会使用const修饰),在用已经存在的类类型对象时创建新对象时会由编译器自动调用

4.2 特性

  1. 是构造函数的一种重载形式(函数名与类型一致)
  2. 拷贝构造函数的参数只能有一个,且得是类类型对象的引用,否则在使用拷贝构造函数会直接报错(引发无穷递归调用)
  3. 如果没有显式定义,编译器会自动生成默认的拷贝构造函数,默认的拷贝构造函数会对内置类型进行处理,按内存存储按字节序完成拷贝,也被称为浅拷贝或者值拷贝

示例1:

#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Init(int year = 2024, int month = 4, int day = 15){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024,4,15);Date d2 = d1; //与下面创建对象d3是等价的,两种写法d2.Print();Date d3(d1);d3.Print();return 0;
}

示例2:

#include <iostream>
using namespace std;
class Stack
{
public:Stack(int n = 4){cout << "Stack()" << endl;int* tmp = (int*)malloc(sizeof(int) * n);if (nullptr == tmp){perror("malloc fail");return;}_arr = tmp;_capacity = n;_size = 0;}void Push(int x){//扩容_arr[_size] = x;_size++;}~Stack(){cout << "~Stack()" << endl;if (_arr){free(_arr);_arr = nullptr;_capacity = 0;_size = 0;}}
private:int* _arr;int _size;int _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);Stack s2 = s1;return 0;
}

代码运行结果:
报错

  编译器生成的默认拷贝构造是不够用,在上述代码中,s2对象使用s1对象的拷贝,由于是浅拷贝,会将s1中的内容原封不动的拷贝给s2,因此s1和s2调用的是同一块空间,在调用析构函数时,s1将空间释放了,但是s2中存放的还是s1的空间,因为还会再释放一次,一块内存空间的多次释放,会造成程序奔溃。同时在对任意一个栈中push数据的时候,另一个栈中的size是不会加的,但是共用的是同一块空间,数据丢失等等问题

示例3:错误写法

#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(Date d) //错误写法 会引发无穷递归{_year = d._year;_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};Date Func(Date d)
{Date tmp(d);return tmp;
}int main()
{Date d1(2024, 4, 15);Func(d1);return 0;
}

在返回一个局部变量时,由于局部变量出作用域就销毁了,所以会将局部变量拷贝给一个临时变量,在给拷贝给临时变量时,又会调用拷贝构造函数,在调用拷贝构造函数时,又会将返回值拷贝给一个临时变量,造成无穷递归

4.3 总结

  1. 在类中,如果没有涉及需要资源管理的内置类型,是可以不写拷贝构造函数的,编译器自动生成的浅拷贝就够用,但是一旦涉及,就需要自己实现拷贝构造函数了
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用

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

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

相关文章

自动化测试Selenium(3)

目录 WebDriver相关API 打印信息 打印title 打印url 浏览器的操作 浏览器最大化 设置浏览器的宽,高 操作浏览器的前进, 后退, 刷新 控制浏览器滚动条 键盘事件 键盘单键用法 键盘组合按键用法 鼠标事件 WebDriver相关API 打印信息 打印title 即打印该网址的标题.…

2023图灵奖揭晓,你怎么看?

Avi Wigderson&#xff0c;以色列裔美国数学家&#xff0c;普林斯顿高级研究学院教授&#xff0c;以其在理论计算机科学和数学中的杰出贡献而闻名。 他在1956年生于以色列海法&#xff0c;后来在1974年至1980年在以色列理工学院学习&#xff0c;获得数学和计算机科学的学士和硕…

量子城域网系列(五):几种典型的量子密钥分发网络组网结构

在上之前文章中&#xff0c;我们介绍了最基本的点对点量子保密通信网络形式以及组网方案&#xff0c;但是显然在实际的应用中&#xff0c;点对点的通信是比较少的&#xff0c;大多还是需要多个终端进行互联才能构成网络。本文我们就讨论一下几种基础的量子密钥分发网络组网结构…

2023年图灵奖颁发给艾维·维格森(Avi Wigderson),浅谈其计算复杂性理论方面做出的重要贡献

Avi Wigderson是一位以色列计算机科学家&#xff0c;他在计算复杂性理论方面做出了重要的贡献&#xff0c;并对现代计算产生了深远的影响。 Wigderson的主要贡献之一是在证明计算复杂性理论中的基本问题的困难性方面。他证明了许多经典问题的困难性&#xff0c;如图论中的图同构…

LeetCode-928. 尽量减少恶意软件的传播 II【深度优先搜索 广度优先搜索 并查集 图 哈希表】

LeetCode-928. 尽量减少恶意软件的传播 II【深度优先搜索 广度优先搜索 并查集 图 哈希表】 题目描述&#xff1a;解题思路一&#xff1a;解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&#xff1a; 给定一个由 n 个节点组成的网络&#xff0c;用 n x n 个邻接矩阵…

网盘——添加好友

关于添加好友&#xff0c;过程如下&#xff1a; A、首先客户端A发送加好友的请求&#xff0c;发送的信息包括双方的用户名 B、当服务器收到请求之后&#xff0c;服务器将数据库中在线用户查找出来&#xff0c;如果客户端B已经是你的好友了&#xff0c;服务器告诉客户端A他已经…

ArkTs

一、概述 ArkTs是由TypeScript扩展而来&#xff0c;在继承TypeScript语法的基础上进行了一系列优化&#xff0c;使开发者能够以更简洁、更自然的方式开发应用。 TypeScript语法: 线上网站:https://www.typescriptlang.org/zh/play 二、TS变量 变量声明: 常量声明: const b…

Ubuntu Vs code配置ROS开发环境

文章目录 1.开发环境2.集成开发环境搭建2.1 安装Ros2.2 安装 Vs code2.3 安装vs code 插件 3.Vs code 配置ROS3.1 创建ROS工作空间3.2 从文件夹启动Vs code3.3 使用Vscode 编译ROS 空间3.4 使用Vs code 创建功能包 4.编写简单Demo实例4.1编写代码4.2编译与执行 1.开发环境 系统…

【学习笔记】Python大数据处理与分析——Matplotlib数据可视化

一、绘图步骤 1、导入第三方库 import matplotlib.pyplot as plt import numpy as np 2、准备数据 x1 np.linspace(1, 10, 5) y1 np.sin(x1) 3、开始绘图 plt.plot(x1, y1, linewidth3) 4、完善图表 plt.title("plot figure") plt.xlabel("value of x&qu…

DC/DC电源模块直流升压变换器电压控制输出5V12V24V转0-50V80V110V150V180V200V250V300V500V800V1000V

特点 效率高达 75%以上1*2英寸标准封装单电压输出可直接焊在PCB 上工作温度: -40℃~75℃阻燃封装&#xff0c;满足UL94-V0 要求温度特性好电压控制输出,输出电压随控制电压线性变化 应用 GRB 系列模块电源是一种DC-DC升压变换器。该模块电源的输入电压分为&#xff1a;4.5~9V、…

基于JavaWeb开发的springboot网约车智能接单规划小程序[附源码]

基于JavaWeb开发的springboot网约车智能接单规划小程序[附源码] &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种…

基于springboot实现大创管理系统【项目源码+论文说明】计算机毕业设计

基于springboot实现大创管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了大创管理系统的开发全过程。通过分析大创管理系统管理的不足&#xff0c;创建了一个计算机管理大创管理系统的方案。文章介…