C++初阶:类与对象(中篇)

目录

  • 2. 类的六个默认成员函数
    • 2.1 构造函数
      • 2.1.1 构造函数的定义方式
    • 2.2 析构函数
      • 2.2.1 析构函数定义方式
    • 2.3拷贝构造函数
      • 2.3.1 拷贝构造函数的定义方式
      • 2.3.2 深拷贝与浅拷贝
    • 2.4 赋值运算符的重载
    • 2.4.1 运算符重载
    • 2.4.2 运算符的重载的定义方式
      • 2.4.3 默认成员函数:赋值运算符重载
      • 2.4.4 前置++,后置++的运算符重载
    • 2.5 类与对象穿插巩固练习:日期类的实现
    • 2.6 const成员函数
    • 2.7 默认成员函数:取地址操作符重载与const取地址

2. 类的六个默认成员函数

  1. 默认成员函数
    <1> 成员函数,即所属与定义类的函数,只针对所属类,只有所属类可以调用的函数,而默认成员函数即为定义类时类会默认生成自带的函数。
    <2> 既然会默认生成,那么一定的这些函数一定非常重要并且不可或缺,究竟什么样的函数会让类定义时要默认生成呢,我们接下来进行学习

2.1 构造函数

  1. 在C语言的学习中,我们定义过各种各样的结构体,这些结构体声明出来,只是单纯开辟了对应的空间,而空间里的值是随机的,因此对新创建结构体变量的初始化是必不可少的,会影响到后续操作。
  2. 对于C++中的类,也是如此,因此对实体化对象的初始化必不可少且十分重要,而我们自主编写时,会经常忘记定义或者调用,所以在C++中就添加了此函数的默认生成,自动调用的内容。

2.1.1 构造函数的定义方式

  1. 构造函数在整个对象的声明周期中会自动调用,且调用一次
  2. 构造函数只负责初始化不会开辟空间
  3. 构造函数的形式:
    <1> 函数名与类名相同
    <2> 无返回值
    <3> 实例化对象会自动调用相应的构造函数
    <4> 构造函数可以重载
  1. 默认生成的构造函数

内置类型:

class Date
{
public:int _year;int _month;int _day;void Print(){cout << _year << ' ' << _month << ' ' << _day << endl;}
};Date d1;
d1.Print();

在这里插入图片描述
自定义类型:

class stack
{
public:int _capacity;int _top;stack(int capacity = 0, int top = 0){cout << "stack()" << endl;_capacity = capacity;_top = top;}
};class queue
{
public:stack push_st;stack pop_st;
};queue q1;

在这里插入图片描述

  1. 虽然编译器会帮助我们生成构造函数,可是默认生成的构造函数并不让人满意
    <1> 默认生成的构造函数对内置类型会赋予随机值
    <2> 对自定义类型会去调用它们的默认构造函数
  2. 所以,在日常中我们还是应该去自己给类定义需要适合的构造函数
  1. 构造函数的定义方式
[类名](传递形参)
{//函数操作
}
class Date
{
private:int _year;int _month;int _day;Date(int year = 0, int month = 0, int day = 0){_year = year;_month = month;_day = day;}
};
  1. 带参数构造函数的传参方式:在实例化对象是直接进行传参,同时也支持缺省参数
Date d1(2024, 3, 8);
Date d2(2024);
  1. C++11中,为默认构造函数对成员变量初始化为随机值的这一操作,做了补丁优化
    我们在定义类的成员变量时,可以在定义处赋予变量初始默认值,这样在我们没有定义构造函数时,自定义类型的成员变量也会有合法的初始值
class Date
{
private:int _year = 2024;int _month = 1;int _day = 1;
};
  1. 默认构造函数不止仅仅是只,我们未定义编译器默认生成的函数
    <1> 未定义编译器默认生成的构造函数
    <2> 没有参数的构造函数
    <3> 参数为全缺省参数的构造函数
    以上三者统称为默认构造函数

2.2 析构函数

  1. 存在资源申请,就一定会存在资源的返还,我们已经知晓了如何创建一个对象,那么,我们如何销毁它呢。
  2. 正常情况下创建的变量会在自己的声明周期结束自动销毁,可是我们动态开辟的空间,这类资源只要程序不结束,我们不去主动销毁那么它就会一直存在,当一段程序需要不停的运行时,这一点就是致命的,因此析构函数对实例化对象资源的管理就尤为重要。

2.2.1 析构函数定义方式

  1. 默认生成的析构函数不会进行资源的深度清理(指针所指向动态开辟的空间),只会进行资源的简单清理
class A
{
private:int* _a;int _b;A(){//销毁_b = 10;//所指向空间不会被释放_a = (int*)malloc(_b * sizeof(int));}
};
  1. 析构函数的特征及其定义方式
    <1> 函数名由:~ + 类名构成
    <2> 没有参数,没有返回值
    <3> 一个类只存在一个析构函数,不存在重载
    <4> 析构函数会在实例化对象生命周期结束时自动调用
~[类名]()
{//操作
}
class B
{
privare:int* _a;;B(){_a = (int*)malloc(sizeof(int));}~B(){cout << "~B()" << endl;free(_a)}
};
  1. 自定义类型的成员变量,默认析构函数会去调用它自己的析构函数
    拓展练习

2.3拷贝构造函数

  1. 使用另一个已经存在的实例化对象,将其作为模板,创建一个与之一样的新对象

2.3.1 拷贝构造函数的定义方式

  1. 拷贝构造函数的特征:
    <1> 拷贝构造函数为构造函数的函数重载
    <2> 拷贝构造函数只有一个参数,参数类型为所处类的类型引用
  1. 补充:拷贝构造函数参数类型必须为引用类型的原因(产生无穷递归)
//err
Date (const Date d)
{}

在这里插入图片描述

2.3.2 深拷贝与浅拷贝

  1. 拷贝构造也为类的默认成员函数之一,当我们不去自己定义时,编译器自主生成一个拷贝构造函数,可是此拷贝构造只会进行值拷贝不会对动态申请得进行拷贝

在这里插入图片描述

  1. 我们将值拷贝称为浅拷贝,将对原变量的深度资源也进行拷贝的操作称为深拷贝。
  2. 当自定义类中没有需要深度拷贝的资源时,我们也可以省略拷贝构造的定义,让编译器自动生成。
  3. 拷贝构造函数使用场景:
    <1> 用已存在的对象创建一个一样的新对象
    <2> 函数参数为类(生成临时变量)
    <3> 函数返回值为类(生成临时变量)
Date d1(2024, 3, 9);
//场景1:
Date d2(d1);void Print(Date d)
{cout << d._year << endl;
}
//场景2
Print(d2);//场景3:
Date f()
{Date d3(2024);return d3;
}
  1. 上述场景为生成临时变量而调用拷贝构造,在一些时候可以使用,引用传参(不改变值),引用返回(原对象不被销毁)来规避这一额外开销。

2.4 赋值运算符的重载

2.4.1 运算符重载

  1. 我们知道编程语言也是一门语言,是用来描述问题与计算机沟通的语言。
  2. C++中,我们引入了类与对象的概念,让计算机可以更好更贴切的描述我们所处的世界。
    <1> 对象是更加复杂,贴近于现实的变量,C中的内置类型变量有着自己运算操作符于运算规则
    <2>可是,内置类型的运算符不能使用于更复杂的类上,这就导致当对进行这类复杂对象间的计算无法进行,由此,C++引入了运算符重载的概念。
  1. 运算符重载的本质是函数,这类函数以类似于运算符的函数调用方式,来达到近似运算符的操作。

2.4.2 运算符的重载的定义方式

[函数返回值] operator[操作符](函数参数)
{//.......
}
  1. 运算符重载的注意事项:
    <1> 不能通过运算符重载来创建原本不存在的运算符,如operator¥
    <2> 不能通过运算符重载来更改原本内置类型操作符的含义
    <3> 操作符.*::sizeof: ?.,这五个操作符不能重载

2.4.3 默认成员函数:赋值运算符重载

  1. 赋值运算符:拥有两个操作数,会返回被赋值后的变量
  2. 赋值运算符的优化:
    <1> 传参时,因为不会改变参数原本的值,所以可以直接传引用(const 类&)
    <2> 因为被赋值的对象不会被销毁,所以可以返回引用(返回自身支持连续赋值)
    <3> 赋值时应进行检测,不能让对象自己给自己赋值
class A
{
public:int _a;int _b;A(int a = 0, int b = 0){_a = a;_b = b;}A& operator=(const A& x){if(&x != *this){_a = x._a;_b = x._b;}return *this;}
};
  1. 注:重载的赋值运算符只能作为类的成员函数,不能作为全局函数(缺少this指针)
  1. 因为是默认成员函数,当我们没有定义时,编译器会自行生成,但与拷贝构造相似,默认生成的赋值运算符重载进行的也是浅拷贝,可能会导致资源的丢失

在这里插入图片描述

2.4.4 前置++,后置++的运算符重载

  1. 我们已经初步学习了运算符的重载,现在我们来学习一下运算符重载中比较特殊的两个运算符,前置++与后置++
  2. 每当需要对一个运算符进行重载时,我们在思考实现逻辑之前,首先应该确定的是运算符有几个操作数,是否有返回值。当我们以这样的视角去观察前置++与后置++这一对操作符时,我们发现它们重载时它们参数是相同,无法进行区分,C++语言在出现了逻辑漏洞,无法形成逻辑的闭环。
  3. 其实,上述无法逻辑闭环的逻辑漏洞在语言的设计上时有出现,这种情况下,一律都会采取特殊化处理,这里的两个++运算符同样如此。

前置++操作符的重载方式:

[返回值] operator++()
{//......
}

后置++操作符的重载方式:

[返回值] operator++(int)
{//......
}

2.5 类与对象穿插巩固练习:日期类的实现

实现目标:

  1. 构造,拷贝构造,析构函数
  2. 赋值运算符==,+,-,前置++,后置++,+=,<,>等运算符的重载

Date.h

class Date
{
private:int _year;int _month;int _day;
public:int GetMonthDay(int year, int month){int MonthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };int day = MonthDay[month];if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0){if (month == 2){day++;}}return day;}void Print(){cout << _year << '-' << _month << '-' << _day << endl;}Date(int year = 2024, int month = 1, int day = 1);//缺省参数在声明处定义Date(const Date& d);~Date();Date& operator=(const Date& d);Date operator+(int day);int operator-(const Date& d);Date operator-(int day);Date& operator+=(int day);Date& operator-=(int day);bool operator==(const Date& d);bool operator>(const Date& d);bool operator<(const Date& d);bool operator>=(const Date& d);bool operator<=(const Date& d);bool operator!=(const Date& d);//前置Date& operator++();//后置Date operator++(int);Date& operator--();Date operator--(int);
};

Date.cpp

  1. 默认成员函数
Date::Date(int year, int month, int day)
{//检查日期合法assert(year >= 1);assert(month >= 1 && month <= 12);assert(GetMonthDay(year, month) >= day && day >= 1);_year = year;_month = month;_day = day;
}Date::Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}Date::~Date()
{}Date& Date::operator=(const Date& d)
{if (&d != this){_year = d._year;_month = d._month;_day = d._day;}return *this;
}
  1. 其他运算符重载
    补充:运算重载的复用顺序
    <1>先+,后+=:创建拷贝对象(拷贝构造1次)+ 返回临时变量(拷贝构造1次)+ 赋值给this指针(拷贝1次)+ 返回this指针
    <2> 先+=,后+:创建拷贝对象(拷贝构造1次)+返回临时变量(拷贝构造1次)
//2.
Date& Date::operator+=(int day)
{//1. 先加后合法化//2. 累加法_day += day;int month_day = GetMonthDay(_year, _month);while (_day > month_day){_day -= month_day;_month++;if (_month > 12){_year++;_month = 1;}month_day = GetMonthDay(_year, _month);}return *this;
}Date Date::operator+(int day)
{Date d(*this);return *this += day;
}//先实现-=
Date& Date::operator-=(int day)
{_day -= day;while (_day < 1){_month--;if (_month < 1){_year--;_month = 12;}int month_day = GetMonthDay(_year, _month);_day += month_day;}return *this;
}Date Date::operator-(int day)
{Date d(*this);return d -= day;
}//差值法
//int Date::operator-(const Date& d)
//{
//	//确定先后,前 - 后
//	Date max(*this);
//	Date min(d);
//
//	if (max._year < min._year)
//	{
//		max = d;
//		min = *this;
//	}
//
//	//计算到1月1日的差值
//	int max_day = 0;
//	for (int i = 1; i < max._month; i++)
//	{
//		max_day += GetMonthDay(max._year, i);
//	}
//	max_day += max._day - 1;
//
//	int min_day = 0;
//	for (int i = 1; i < min._month; i++)
//	{
//		min_day += GetMonthDay(min._year, i);
//	}
//	min_day += min._day - 1;
//
//	//两年1月1日之间的差值
//	int year_day = 0;
//	while (min._year < max._year)
//	{
//		year_day += 365;
//		if ((min._year % 4 == 0 && min._year % 100 != 0) || min._year % 400 == 0)
//		{
//			year_day++;
//		}
//		min._year++;
//	}
//
//	return year_day - min_day + max_day;
//}//累计法
int Date::operator-(const Date& d)
{Date max(*this);Date min(d);if (max._year < min._year){max = d;min = *this;}int day = 0;while (max != min){min++;day++;}return day;
}

-操作符重载差值法思路图解:

在这里插入图片描述

  1. 逻辑比较运算符:
bool Date::operator==(const Date& d)
{if (_year == d._year && _month == d._month && _day == d._day){return true;}return false;
}bool Date::operator>(const Date& d)
{if (_year > d._year){return true;}else if (_year == d._year && _month > d._month){return true;}else if (_year == d._year && _month > d._month && _day > d._day){return true;}return false;
}bool Date::operator<(const Date& d)
{return !(*this == d) && !(*this > d);
}bool Date::operator>=(const Date& d)
{return *this > d || *this == d;
}bool Date::operator<=(const Date& d)
{return *this < d || *this == d;
}bool Date::operator!=(const Date& d)
{return !(*this == d);
}//前置
Date& Date::operator++()
{return *this += 1;
}
//后置
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}Date& Date::operator--()
{return *this -= 1;
}Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}

2.6 const成员函数

  1. 被this指针被修饰const修饰的成员函数即被称为const成员函数(const Date* const this),被const修饰的this指针其指向的内容不能被改变。
  2. 因为this指针为隐藏参数,我们无法直接对其进行修饰,C++中采用了后缀修饰的方式,具体如下:
Date::Print() const
{count << _year << '-' << _month << '-' << _day << endl;
}
  1. 函数传参时的权限缩可以缩小但不能扩大,所以
    <1> const对象不可以调用非const成员函数(权限扩大,const Date* const this => Date* const this)
    <2> 非const对象可以调用const成员函数(权限缩小,Date* const this => const Date* const this)
    <3> const成员函数内不可以调用其它的非const成员函数(权限扩大)
    <4> 非const成员函数内可以调用其它的const成员函数(权限缩小)

2.7 默认成员函数:取地址操作符重载与const取地址

这两个默认成员函数一般不用我们自己定义,编译器默认生成的已经满足绝大部分情况。(当想要别人获得额外的一些信息时,才会选择重载)

Date* operator&()
{return this;
}const Date* operator&() const
{return this;
}

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

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

相关文章

HTML使用

文章目录 一、简介二、HTML快速入门三、基础标签四、图片、音频、视频标签五、超链接标签六、列表标签七、表格标签八、布局标签九、表单标签十、表单向标签 一、简介 二、HTML快速入门 ​ <html><head><title>你好</title></head><body>再…

功能强大使用简单的截图/贴图工具,PixPin

一、下载链接 PixPin 截图/贴图/长截图/文字识别/标注 | PixPin 截图/贴图/长截图/文字识别/标注 (pixpinapp.com) 二、功能 截图/贴图/长截图/文字识别/标注 三、安装教程 根据提示安装即可&#xff1a; 四、快捷键 1.软件自带快捷键&#xff08;右击PixPin查看 &#xff09…

微信小程序-入门

1.下载和安装Npm&#xff1a;Npm https://docs.npmjs.com/downloading-and-installing-node-js-and-npm 或者 https://nodejs.org/en/download/ 未安装npm 提示 以下以安装node安装包为例 按任意键继续 安装完成后 2. 下载和安装小程序开发工具 &#xff1a;https:/…

安信可IDE(AiThinker_IDE)编译ESP8266工程方法

0 工具准备 AiThinker_IDE.exe ESP8266工程源码 1 安信可IDE&#xff08;AiThinker_IDE&#xff09;编译ESP8266工程方法 1.1 解压ESP8266工程文件夹 我们这里使用的是NON-OS_SDK&#xff0c;将NON-OS_SDK中的1_UART文件夹解压到工作目录即可 我这里解压到了桌面&#xff0c…

T1 小美的数组询问(15分) - 美团编程题 题解

考试平台&#xff1a; 牛客网 题目类型&#xff1a; 30道单选题&#xff08;60分&#xff09; 2 道编程题 &#xff08;15分 25分&#xff09; 考试时间&#xff1a; 2024-03-09 &#xff08;两小时&#xff09; 题目描述 小美拿到了一个由正整数组成的数组&#xff0c;但其中…

Draco点云压缩测试

ref&#xff1a;https://github.com/google/dracohttps://codelabs.developers.google.com/codelabs/draco-3d/index.html#6 Draco Draco 是一个用于编码压缩和解压缩 3D 几何网格和点云的库&#xff0c;从而改进 3D 图形的存储和传输该代码支持压缩点、连接信息、纹理坐标、颜…

NTFS安全权限

NTFS是新技术文件系统&#xff08;New Technology File System&#xff09;的缩写&#xff0c;是一种用于Windows操作系统的文件系统。NTFS提供了高级的功能和性能&#xff0c;包括文件和目录的权限控制、加密、压缩以及日志等。它被广泛应用于Windows NT、Windows 2000、Windo…

ubuntu 运行opencv_sample遇到的问题

首先我遇到的问题就是摄像头连接不上 勾选最后一个 然后是 usb接口问题 点击虚拟机设置 我的是改为 3 就可以啦

谷歌seo外链和内链区别?

什么是外链&#xff1f;外链就是其他网站链接到你的网站&#xff0c;别人的网站提到了你的网站&#xff0c;并愿意放你网站的链接在上面&#xff0c;这就是外链&#xff0c;外链的重点&#xff0c;就在于这是外部网站给你网站的推荐&#xff0c;意味着别人认可你的内容或服务&a…

JL15-80/11电流继电器 过电流瞬时动作 电磁式结构 80A 一开一闭

JL15电流继电器 系列型号 JL15-1.5/11电流继电器JL15-2.5/11电流继电器 JL15-5/11电流继电器JL15-10/11电流继电器 JL15-15/11电流继电器JL15-20/11电流继电器 JL15-30/11电流继电器JL15-40/11电流继电器 JL15-60/11电流继电器JL15-80/11电流继电器 JL15-100/11电流继电器JL15…

关于2024/3/4号的学习总结

今天没有早八&#xff0c;八点之钱起床了&#xff0c;上午背了半小时的单词&#xff0c;然后就在写top100&#xff0c;目前中等和简单写了30题&#xff0c;基本上都没有看题解。我自己也整理下&#xff0c;每一题的思路&#xff0c;这样子&#xff0c;也会让我至少拥有做模板题…

JavaScript代码混淆与防格式化功能详解

在前端开发中&#xff0c;为了增加代码的安全性&#xff0c;防止恶意分析和逆向工程&#xff0c;有时候会采用一些防格式化的技术。这些技术主要通过混淆和难以阅读的方式来防止代码的易读性&#xff0c;提高代码的复杂度&#xff0c;增加攻击者分析的难度。 1. 代码压缩与混淆…

机器学习--循环神经网路(RNN)2

在这篇文章中&#xff0c;我们介绍一下其他的RNN。 一.深层RNN 循环神经网络的架构是可以任意设计的&#xff0c;之前提到的 RNN 只有一个隐藏层&#xff0c;但 RNN 也可以是深层的。比如把 xt 丢进去之后&#xff0c;它可以通过一个隐藏层&#xff0c;再通过第二个隐藏层&am…

JavaEE企业开发新技术

目录 2.1 Class对象基本概念 1、概念 2.2 Class对象的获取方式 2.3基本数据类型的Class对象 1、概念 2.4 反射的基本概念 概念 2.5 Class对象的基本使用-1 2.6 Class对象的基本使用-2 newInstance()和new()区别&#xff1a; 2.1 Class对象基本概念 1、概念 反射的…

[LeetCode][102]二叉树的层序遍历——遍历结果中每一层明显区分

题目 102. 二叉树的层序遍历 给定二叉树的根节点 root&#xff0c;返回节点值的层序遍历结果。即逐层地&#xff0c;从左到右访问所有节点。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]] 示例 2&#xff1a; 输入…

Java设计模式:建造者模式之经典与流式的三种实现(四)

本文将深入探讨Java中建造者模式的两种实现方式&#xff1a;经典建造者与流式建造者。建造者模式是一种创建型设计模式&#xff0c;它允许你构建复杂对象的步骤分解&#xff0c;使得对象的创建过程更加清晰和灵活。我们将通过示例代码详细解释这两种实现方式&#xff0c;并分析…

Redux 与 Vuex:探索它们的设计思想及应用差异

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

图像压缩神器:使用wxPython和Pillow快速压缩JPEG文件

导语&#xff1a; 在数字时代&#xff0c;我们经常处理大量的图像文件&#xff0c;无论是个人照片、网络图片还是工作中的设计素材。然而&#xff0c;随着图像数量的增多&#xff0c;存储和传输这些文件可能会成为一个挑战。幸运的是&#xff0c;我们可以利用Python编程和两个强…

【Git】项目源码迁移到另一个gitlab(保留原来提交历史记录)

目录 前情提要迁移方案IDEA远程仓库管理团队其他成员切换gitgit命令操作界面 前情提要 公司原来是自己私有部署的gitlab。有了研发云后就希望将代码推送到研发云的代码仓库上。这时候需要迁移并保留原来提交的历史记录。 迁移方案 登录新的gitlab(代码仓库)新建空白项目获取…

如何搭建selenium自动化测试框架?selenium自动化测试环境搭建(webdriver+Python框架)

一、安装Python 选择Python版本后&#xff0c;进入Python官方网站下载适配机型版本&#xff1a;http://www.python.org/ 二、安装setuptools和pip setuptools下载地址&#xff1a;https://pypi.python.org/pypi/setuptools pip下载地址&#xff1a;https://pypi.python.org/py…