【C++初阶】类和对象(中)

目录

一.类的6个默认成员函数

1.知识引入 

​编辑 2.构造函数 

(1)概念 

(2)语法特性

(3)特征 

①问题引入1

②问题引入2  (缺少默认构造函数)

3.析构函数

(1)概念

(2)特性

4.拷贝构造函数 

(1)概念

(2)特征

①拷贝构造函数是构造函数的一个重载形式。

②拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。 

③若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象的内置类型成员按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。自定义类型成员调用它的拷贝构造。

5.赋值运算符重载  

 (1)运算符重载

① 语法及注意事项

(2)赋值运算符重载

二.总结 


一.类的6个默认成员函数

1.知识引入 

如果一个类中什么成员都没有,简称为空类

空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成6个默认成员函数。

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数

  • 示例 

 2.构造函数 

(1)概念 

构造函数是一个特殊的成员函数,名字与类名相同,创建类 类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。 

  • 注意

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象

(2)语法特性

  1. 函数名与类名相同。
  2. 无返回值。(不是void,是就不需要写)
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载。
  •  示例 
class Date
{
public:Date()//构造函数{_year = 1;_month = 1;_day = 1;}/*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();//调用带参构造函数/*Date d2(2024,2,8);d2.Print();*/return 0;
}
  • 注意
 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)//Date d3();

 通常情况只写一个构造函数,避免调用时存在歧义。

 (3)特征 

①问题引入1

关于编译器生成的默认成员函数,会有以下疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用?? 

class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;d1.Print();return 0;
}

  • 解答

 C++把类型分成内置类型(基本类型)自定义类型。内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型。默认生成的构造函数,对于内置类型不做处理,自定义类型会去调用他的默认构造函数。 

class A
{
public:A(){cout <<"A()" << endl;_a = 0;}
private:int _a;
};
class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;//也可以在这里给缺省值int _month;int _day;A _aa;
};int main()
{Date d1;d1.Print();return 0;
}
  •  结果显示

 ②问题引入2  (缺少默认构造函数)
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private://也可以在声明时给缺省值int _year=1;int _month=1;int _day;};int main()
{Date d1;d1.Print();return 0;
}

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数全缺省构造函数我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

问题存在:我们写了一个带参数的构造函数,编译器就不再给我们生成构造函数了。

  • 修改 
class Date
{
public:Date(){_year = 1;_month = 1;_day = 1;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private://也可以在声明时给缺省值int _year=1;int _month=1;int _day;};int main()
{Date d1;d1.Print();return 0;
}

3.析构函数

 (1)概念

析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

(2)特性

1. 析构函数名是在类名前加上字符 ~。

2. 无参数无返回值类型。
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载。

4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

class Date
{
public:Date(){_year = 1;_month = 1;_day = 1;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}~Date(){cout << this << endl;cout << "~Date()" << endl;}
private://也可以在声明时给缺省值int _year=1;int _month=1;int _day;};
void func()
{Date d2;
}
int main()
{func();Date d1;d1.Print();return 0;
}

 

4.拷贝构造函数 

(1)概念

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

(2)特征

①拷贝构造函数是构造函数的一个重载形式
//拷贝构造函数
class Date
{
public:Date(int year = 2024, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//Date(const Date& d) // 错误写法:编译报错,会引发无穷递归Date(const Date& d) // 正确写法{_year = d._year;_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2024,1,28);Date d2(d1);return 0;
}
②拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。 
  •  C++规定自定义类型的拷贝都会调用拷贝构造

而传引用传参则不再需要调用拷贝构造。 

  • 为什么会引发无穷递归 

③若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象的内置类型成员按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。自定义类型成员调用它的拷贝构造。
class Date
{
public:Date(int year = 2024, 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(2024,1,28);Date d2(d1);d1.Print();d2.Print();return 0;
}
  • 深拷贝 
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};
int main()
{Stack st1;Stack s2(st1);return 0;
}

程序最终崩溃,这是为什么呢?

  • 问题分析 

  • 代码修改(手写拷贝构造)
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}//Stack st2(st1)Stack(const Stack& s){DataType* tmp = (DataType*)malloc(sizeof(s._capacity * (sizeof(DataType))));if (tmp == nullptr){perror("malloc fail");exit(-1);}memcpy(tmp, s._array, sizeof(DataType) * s._size);_array = tmp;_size = s._size;_capacity = s._capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};
int main()
{Stack st1;Stack s2(st1);return 0;
}

5.赋值运算符重载  

 (1)运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。 

① 语法及注意事项

函数原型:返回值类型 operator操作符(参数列表) 

  • 注意事项
  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  5. .*    ::    sizeof    ?:    .注意注意以上5个运算符不能重载。
  • 比较两个日期相等及小于(传统写法) 
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//private:int _year;int _month;int _day;
};
//判断日期相等
bool DataEqual(const Date& x, const Date& y)
{return x._year == y._year&& x._month == y._month&& x._day == y._day;
}
//判断日期小于
bool DateLess(const Date& x, const Date& y)
{if (x._year < y._year){return true;}else if (x._year == y._year){if (x._month < y._month){return true;}else if (x._month == y._month){return x._day < y._day;}}return false;
}
int main()
{Date d1(2024, 1, 28);Date d2(2024, 2, 27);cout << DataEqual(d1, d2) << endl;cout << DateLess(d1, d2) << endl;return 0;
}

缺陷:由于函数名可能会存在取名字不规范的情况,将导致不知道这个函数的作用是什么。

因此采用运算符重载,来更容易让看代码的人知道这个代码的作用,增强代码可读性。 

  • 运算符重载写法 
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//private:int _year;int _month;int _day;
};
//判断日期相等
bool operator==(const Date& x, const Date& y)
{return x._year == y._year&& x._month == y._month&& x._day == y._day;
}
//判断日期小于
bool operator<(const Date& x, const Date& y)
{if (x._year < y._year){return true;}else if (x._year == y._year){if (x._month < y._month){return true;}else if (x._month == y._month){return x._day < y._day;}}return false;
}
int main()
{Date d1(2024, 1, 28);Date d2(2024, 2, 27);cout << (d1==d2) << endl;//cout<<(operator==(d1,d2)<<endl;cout << (d1<d2) << endl;return 0;
}
  • 注意 

这里代码能执行是因为我们将类内的成员设置成了公有权限,否则在类外面是不能访问类里面的成员的。

  • 处理方式(将类外函数放入类内)
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//判断日期相等bool operator==( const Date& y){return _year == y._year&& _month == y._month&& _day == y._day;}//判断日期小于bool operator<( const Date& y){if (_year < y._year){return true;}else if (_year == y._year){if (_month < y._month){return true;}else if (_month == y._month){return _day < y._day;}}return false;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 1, 28);Date d2(2024, 2, 27);cout <<d1.operator==(d2) << endl;cout << d1.operator<(d2) << endl;cout << (d1==d2) << endl;cout << (d1<d2) << endl;return 0;
}

(2)赋值运算符重载

赋值运算符重载格式 :

  1.  参数类型:const T&,传递引用可以提高传参效率
  2. 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  3. 检测是否自己给自己赋值
  4. 返回*this :要复合连续赋值的含义
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//d1=d2void operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 1, 28);Date d2(2024, 2, 27)Date d3(d1);//拷贝构造,同类型一个存在的对象进行初始化要创建的对象d1 = d2;//已经存在的对象,一个拷贝赋值给另一个d1.Print();d2.Print();return 0;
}
  • 返回值问题 
d1=d2=d3;

我们知道赋值操作是将右操作数赋值给左操作数,然后将左操作数作为返回值,因此该赋值函数需要有明确的返回值类型。因此要对上方代码进行部分修改:

    Date& operator=(const Date& d)//这里的“&”是引用{if (this != &d)//这里的“&”是取地址{_year = d._year;_month = d._month;_day = d._day;}return *this;}
  • 赋值运算符只能重载成类的成员函数不能重载成全局函数  
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}int _year;int _month;int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}
// 编译失败:
// error C2801: “operator =”必须是非静态成员

原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现 一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值 运算符重载只能是类的成员函数。 

 

二.总结 

 

  

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

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

相关文章

pclpy SOR去除异常值(统计滤波)

pclpy SOR去除异常值-统计滤波 一、算法原理1.背景2.原理 二、代码三、结果1.原点云2.sor处理后的点云&#xff08;内点&#xff09;3.sor处理后的点云&#xff08;外点&#xff09; 四、相关数据 一、算法原理 1.背景 激光扫描通常会生成不同点密度的点云数据集。此外&#…

【OpenSSH+Jenkins搭建项目自动化部署】

OpenSSHJenkins搭建项目自动化部署 一、Windows安装OpenSSH1.下载2.解压3.安装4.启停服务5.SSH免密登录 二、Jenkins安装1.下载2.安装启动3.登录 三、项目自动化部署1.SSH配置2.项目配置3.权限控制 一、Windows安装OpenSSH 1.下载 https://github.com/PowerShell/Win32-0penS…

常见的排序算法整理

1.冒泡排序 1.1 冒泡排序普通版 每次冒泡过程都是从数列的第一个元素开始&#xff0c;然后依次和剩余的元素进行比较&#xff0c;若小于相邻元素&#xff0c;则交换两者位置&#xff0c;同时将较大元素作为下一个比较的基准元素&#xff0c;继续将该元素与其相邻的元素进行比…

用于扫描机密的开源解决方案

TruffleHog 最初是在 2016 年独立创作的一个研究工具。当发布它时&#xff0c;没有工具扫描 Git 修订历史记录以获取秘密。我的预感是旧版本的代码中隐藏着很多秘密&#xff0c;但没有工具可以查找它们。 我的预感是对的。该工具迅速流行并变得非常流行。如今&#xff0c;它在…

数据库管理-第153期 Oracle Vector DB AI-05(20240221)

数据库管理153期 2024-02-21 数据库管理-第153期 Oracle Vector DB & AI-05&#xff08;20240221&#xff09;1 Oracle Vector的其他特性示例1&#xff1a;示例2 2 简单使用Oracle Vector环境创建包含Vector数据类型的表插入向量数据 总结 数据库管理-第153期 Oracle Vecto…

启动node服务报错Error: listen EACCES: permission denied 0.0.0.0:5000

启动node服务报错&#xff1a; 解决方案&#xff1a; 将监听端口改成3000或者其他 修改后结果&#xff1a; 参考原文&#xff1a; Error: listen EACCES: permission denied_error when starting dev server: error: listen eacc-CSDN博客

板块一 Servlet编程:第八节 文件上传下载操作 来自【汤米尼克的JavaEE全套教程专栏】

板块一 Servlet编程&#xff1a;第八节 文件的上传下载操作 一、文件上传&#xff08;1&#xff09;前端内容&#xff08;2&#xff09;后端内容 二、文件下载&#xff08;1&#xff09;前端的超链接下载&#xff08;2&#xff09;后端下载 在之前的内容中我们终于结束了Servle…

【云原生】Docker 安全与CA证书生成

目录 容器的安全行问题 Docker 容器与虚拟机的区别 Docker 存在的安全问题 1.Docker 自身漏洞 2.Docker 源码问题 Docker 架构缺陷与安全机制 1. 容器之间的局域网攻击 2. DDoS 攻击耗尽资源 3. 有漏洞的系统调用 4. 共享root用户权限 Docker 安全基线标准 1. 内…

sympy奇异函数

文章目录 简介SingularityFunctionrewrite 简介 奇异函数是一类不连续函数&#xff0c;可用麦考利括号表示为 f ( t ) < t − t 0 > n f(t)\lt t-t_0\gt^n f(t)<t−t0​>n 当 n < 0 n\lt 0 n<0时&#xff0c;记 N − n − 1 ≥ 0 N-n-1\geq0 N−n−1≥0&…

【云原生】持续集成持续部署

本文主要总结CI/CD的流程&#xff0c;不会详细介绍每个知识点。 啥是集成&#xff1f;啥是部署&#xff1f; 集成&#xff0c;就是把应用程序、相关环境、配置全局打包放在一个容器中的操作。部署就不解释了。 CI/CD 如果是自己手动部署的话&#xff0c;流程应该是这样的&am…

Android 沉浸式状态栏

过时的API //设置默认隐藏虚拟按键&#xff0c;虚拟按键显示后为半透明protected open fun hideNavigationBarAndFullScreen() {val flags: Int// This work only for android 4.4flags if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {// This work only for a…

【算法与数据结构】797、LeetCode所有可能的路径

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;有向无环图&#xff08;Directed acyclic graph, DAG&#xff09;是图论中的一个概念&#xff0c;它指…

SQL注入之DNSLog外带注入

一、认识&#xff1a; 什么是dnslog呢&#xff1f; DNS就是域名解析服务&#xff0c;把一个域名转换成对应的IP地址&#xff0c;转换完成之后&#xff0c;DNS服务器就会有一个日志记录本次转换的时间、域名、域名对应的ip、请求方的一些信息&#xff0c;这个日志就叫DNSLog。…

2.21作业

使用多进程完成两个文件的拷贝&#xff0c;父进程拷贝前一半&#xff0c;子进程拷贝后一半&#xff0c;父进程回收子进程的资源。 程序代码&#xff1a; #include <myhead.h>typedef struct {int start;//拷贝的起始位置int end;//拷贝的结束位置int source_fd;//被拷…

什么是正定矩阵?Positive Definite Matrices (done)

正定矩阵的定义&#xff1a;https://baike.baidu.com/item/%E6%AD%A3%E5%AE%9A%E7%9F%A9%E9%98%B5/11030459 正定矩阵的作用、验证视频&#xff1a;https://www.bilibili.com/video/BV1Ag411M76G/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c…

详解 IT/OT 融合的五层架构(从PLC/SCADA到MES/ERP)

作为一个电气自动化的从业者&#xff0c;有必要搞懂下面术语的意思。 IT&#xff1a;Information Technology的缩写&#xff0c;指信息技术&#xff1b; OT&#xff1a;Operational Technology的缩写&#xff0c;指操作层面的技术&#xff0c;比如运营技术&#xff1b;CT&…

5、电源管理入门之 arm-scmi和mailbox核间通信

目录 1. 整体架构介绍 2 Linux中reset模块 2.1 Reset consumer 2.2 Reset provider 3. Linux SCMI reset通信 3.1 SCMI reset协议初始化 3.2 SCMI reset消息收发 4. SCP中reset 4.1 固件新增module 4.2 scmi_reset_domain初始化 4.3 scmi_reset_domain消息处理 4.3…

JavaScript数据类型学习脑图

字符串&#xff08;String&#xff09;、数字(Number)、布尔(Boolean)、数组(Array)、对象(Object)、空&#xff08;Null&#xff09;、未定义&#xff08;Undefined&#xff09;

跨境电商本土化运营:深度融合本地市场,提升用户体验与市场份额

随着全球经济的不断发展&#xff0c;跨境电商在国际贸易中扮演着越来越重要的角色。然而&#xff0c;单一地面对全球市场可能并不足以满足用户的多样化需求&#xff0c;因此&#xff0c;跨境电商需要与本地市场深度融合&#xff0c;实现本土化运营。本文Nox聚星将和大家探讨跨境…

Linux CAfile 文件下的/ca-bundle.crt怎么生成的

在配置Linux Nginx SSL证书后&#xff0c;通过服务器访问域名时发现&#xff0c;服务器返回的CA证书是&#xff1a;/etc/pki/tls/certs/ca-bundle.crt 正式我在使用Spring Native安装了Docker自动生成的&#xff0c;而且开启了Docker的自启动&#xff0c;如果你和我一样&#x…