C++-类和对象(上)

news2024/11/28 23:44:57

类和对象(上)

  • 一,构造函数
    • 1,概念
    • 2,特性
  • 二,析构函数
    • 1,概念
    • 2,特性
  • 三,拷贝构造
    • 1,概念
    • 2,特性
  • 四,运算符重载
    • 1,概念
    • 2,赋值运算符重载
  • 五,&重载与const& 重载
  • 六,总结

一,构造函数

1,概念

class Stack
{
public:
	void Init(int n = 4)
	{
		_a = (int*)malloc(sizeof(int) * n);
		if (!_a)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capcity = n;
	}
	void Destroy()
	{
		if (_a)
		{
			free(_a);
			_a = nullptr;
			_top = _capcity = 0;
		}
	}
private:
	int* _a;
	int _top;
	int _capcity;
};

int main()
{
	Stack st;
	st.Init();
	return 0;
}

由于C++的类内允许定义函数,但是像上述那样需要我们去手动的初始化对象很容易玩掉。于是C++中提出了构造函数这一概念。

构造函数 :构造函数是一个特殊的成员函数,名字与类名相同,在类的实例化时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象的整个生命周期内只调用一次。
注意:构造函数不是用来创建对象的,而是用来初始化对象的,构造函数是没有返回值的,且支持函数重载。

所以,一般在C++中是这样写的:

class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (int*)malloc(sizeof(int) * n);
		if (!_a)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capcity = n;
	}
private:
	int* _a;
	int _top;
	int _capcity;
};
int main()
{
	Stack st1;
	Stack st2(8);
	return 0;
}

在这里插入图片描述

切忌这样使用无参的构造函数:

Stack st3();

这会与函数的声明冲突,函数名为st3,参数为空,返回值为Stack的函数声明。

2,特性

  • 函数名与类名相同
  • 无返回值
  • 类的实例化时编译器自动调用其构造函数
  • 支持函数重载
  • 如果没有显示的定义构造函数,编译器会自动生成一个默认构造函数,一旦用户显示定义了构造函数,那么编译器就不会自动生成
  • 编译器自动生成的默认构造函数,对内置类型不做处理,对自定义类型会去调用其默认构造函数
class Queue
{
public:

private:
	Stack st1;
	Stack st2;
	int _size;
};
int main()
{
	Queue q;
	return 0;
}

在这里插入图片描述

即使Queue这个类没有显示定义构造函数,所以编译器自动生成了一个默认构造函数,对自定义类型会去调用其默认构造,对内置类型不做处理(可以看到编译器也对_size进行了初始化,这只是针对本编译器会这样做,不具有普遍性)。

  • 针对编译器自动生成的默认构造函数对内置类型不做处理,在C++ 11中提出允许给非静态成员变量提供一个缺省值,即在类的实例化时未对某个成员变量初始化,那么其的值就为所给的缺省值。
class Queue
{
public:

private:
	Stack st1;
	Stack st2;
	int _size = 1;
};
int main()
{
	Queue q;
	return 0;
}

在这里插入图片描述

  • 无参的构造函数与全缺省的构造函数与编译器自动生成的都是默认构造函数,且默认构造函数只允许有一个。

二,析构函数

1,概念

void Destroy()
{
	if (_a)
	{
		free(_a);
		_a = nullptr;
		_top = _capcity = 0;
	}
}

与构造函数的功能相反,当我们定义的对象涉及到动态内存开辟时,之前我们会手动的去调用Destroy函数去进行资源管理,析构函数在对象销毁的时候自动调用,完成对象中资源的清理工作,且函数名为 ~类名 ,没有参数,不支持函数重载。

class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (int*)malloc(sizeof(int) * n);
		if (!_a)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capcity = n;
	}
	~Stack()
	{
		if (_a)
		{
			free(_a);
			_a = nullptr;
			_top = 0;
			_capcity = 0;
		}
	}
private:
	int* _a;
	int _top;
	int _capcity;
};

2,特性

  • 函数名 ~类名
  • 无参数
  • 不支持函数重载
  • 对象销毁时自动调用
  • 当用户没有显示的去写析构函数时,编译器会自动生成一个析构函数。
  • 编译器自动生成的析构函数,对内置类型不做处理,对于自定义类型会去调用其析构函数。
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (int*)malloc(sizeof(int) * n);
		if (!_a)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capcity = n;
	}
	
private:
	int* _a;
	int _top;
	int _capcity;
};
void test1()
{
	Stack st1(6);
}
int main()
{
	test1();
	return 0;
}

Stack 类中的成员变量都是内置类型,编译器自动生成的析构函数不对其做处理,导致_a指向的空间没有被释放,造成内存泄露。

  • 如果类中没有涉及申请资源时,析构函数可以不用写,直接使用编译器自动生成的析构函数,像Stack类这样的涉及申请资源时,一定要写析构函数

三,拷贝构造

1,概念

拷贝构造函数是构造函数的一种重载形式,只有一个参数,是本类对象的引用(一般用const 修饰),用已有对象去初始化创建新的对象时,会自动调用拷贝构造。

2,特性

  • 拷贝构造函数是构造函数的一种重载形式
  • 拷贝构造函数的参数只有一个且必须是同类对象的引用,使用传值的方式编译器会报错,因为会发生无穷递归。
    如果调用拷贝构造使用传值的方式:由于传值传参会发生拷贝构造,也就是说我们还没调用到拷贝构造之前,要现发生一次拷贝构造,依次反复,无穷的递归下去。
  • 如果没有显示的定义拷贝构造函数,编译器会自动生成一个拷贝构造。其对内置类型进行简单的值拷贝,对自定义类型会调用其拷贝构造函数。
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2023, 2, 10);
	Date d2(d1);
	return 0;
}

在这里插入图片描述

  • 当类中涉及到资源的申请时,一定也要写显示的拷贝构造函数,否则自动生成的是浅拷贝。
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (int*)malloc(sizeof(int) * n);
		if (!_a)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capcity = n;
	}
	~Stack()
	{
		if (_a)
		{
			free(_a);
			_a = nullptr;
			_top = 0;
			_capcity = 0;
		}
	}
private:
	int* _a;
	int _top;
	int _capcity;
};
int main()
{
	Stack st1;
	Stack st2(st1);
	return 0;
}

在这里插入图片描述

可以看到如果使用把编译器提供的拷贝构造,会发生浅拷贝,会导致一系列错误的出现。

Stack(const Stack& st)
	{
		if (this != &st)
		{
			_a = (int*)malloc(sizeof(int) * st._capcity);
			if (!_a)
			{
				perror("malloc fail");
				exit(-1);
			}
			memcpy(_a, st._a, sizeof(int) * st._capcity);
			_top = st._top;
			_capcity = st._capcity;
		}
	}

四,运算符重载

1,概念

int main()
{
	vector<int> a(4, 0);
	for (int i = 0; i < 4; i++)
	{
		a[i] = i;
	}
	for (int i = 0; i < 4; i++)
	{
		cout << a[i] << " ";
	}
	return 0;
}

在这里插入图片描述

vector是STL库中提供的顺序表这一数据结构,但是你发现他居然能像数组一样使用[ ]来访问其成员,其实这是重载了[ ]这个运算符,本质上是函数调用。

运算符重载: C++中为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有返回值,函数名,参数列表,且与普通函数类似。其函数名为operator(关键字)+ 要重载的运算符

例如,我们自己实现一个数组类来运用一下这个运算符重载:

class Array
{
public:
	int& operator[](int i)
	{
		return a[i];
	}
private:
	int a[10];
	int size = 0;
};

int main()
{
	Array arr1;
	for (int i = 0; i < 5; i++)
	{
		arr1[i] = i;
	}
	for (int i = 0; i < 5; i++)
	{
		cout << arr1[i] << " ";
	}
	return 0;
}

在这里插入图片描述
注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载运算符必须有一个参数是自定义类型的
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  • .* :: sizeof ?: . 注意以上5个运算符不能重载。

2,赋值运算符重载

1,运算符重载格式

  • 参数类型:const T&,传递引用可以提高传参效率,同时也防止了由于赋值的顺序产生-错误。
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义

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

3,用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator[](const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
	}
private:
	int _year;
	int _month;
	int _day;
};

五,&重载与const& 重载

这两个默认成员函数一般不用自己实现,编译器会默认生成。

class Date
{
public :
Date* operator&()
{
	return this ;

}
const Date* operator&()const
{
	return this ;
}
private :
	int _year ; // 年
	int _month ; // 月
	int _day ; // 日
};

六,总结

1,类的6个默认成员函数

  • 构造函数
  • 析构函数
  • 拷贝构造函数
  • 赋值运算符重载
  • &运算符重载
  • const& 运算符重载

2,默认成员函数共同的性质就是,用户不主动提供时,编译器会自动提供默认成员函数。
3,编译器自动提供的构造函数与析构函数,对内置类型不做处理,对与自定义类型会调用其构造函数和析构函数。
4,编译器自动提供的赋值运算符重载 拷贝构造,对内置类型会进行值拷贝,对自定义类型调用其赋值运算符重载和拷贝构造。
5,C++ 11 提供了可以给内置类型提供缺省值。

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

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

相关文章

联合培养博士经历对于国内就业有优势吗?

2023年国家留学基金委&#xff08;CSC&#xff09;申请在即&#xff0c;很多在读博士在关心申报的同时&#xff0c;也对联培经历能否有助于国内就业心中存疑&#xff0c;故此知识人网小编重点解答此问题。之前&#xff0c;我们在“CSC联合培养-国内在读博士出国的绝佳选择”一文…

【论文解读|KDD2020】AKT. Context-Aware Attentive Knowledge Tracing

文章目录摘要1 引言1.1 贡献3 模型3.4 基于Rasch模型的嵌入摘要 知识追踪(KT)是指根据学习者在教育应用中的过去表现预测未来学习者表现的问题。KT最近使用灵活的基于深度神经网络的模型的发展在这一任务中表现出色。然而&#xff0c;这些模型通常提供有限的可解释性&#xff…

HTML画布与SVG(Canvas vs. SVG)

目录 画布(Canvas) 什么是 Canvas&#xff1f; 创建 Canvas 元素 通过 JavaScript 来绘制 理解坐标 更多 Canvas 实例 实例 - 线条 实例 - 圆形 实例 - 渐变 实例 - 图像 相关页面 SVG (Scalable Vector Graphics) 什么是 SVG&#xff1f; SVG 的优势 浏览器支持…

Springboot+Vue java毕业论文选题管理系统

在分析并得出使用者对程序的功能要求时&#xff0c;就可以进行程序设计了。如图展示的就是管理员功能结构图。 系统实现前端技术&#xff1a;nodejsvueelementui 前端&#xff1a;HTML5,CSS3、JavaScript、VUE 系统分为不同的层次&#xff1a;视图层&#xff08;vue页面&#…

bert处理超过512的长文本(强制改变位置编码position_embeddings )

最近在做 NER 任务的时候&#xff0c;需要处理最长为 1024 个字符的文本&#xff0c;BERT 模型最长的位置编码是 512 个字符&#xff0c;超过512的部分没有位置编码可以用了 处理措施&#xff1a; 将bert的位置编码认为修改成&#xff08;11024&#xff09;&#xff0c;前512…

【C++】类和对象(二)

目录 一、默认成员函数 二、构造函数 1、构造函数概念 2、构造函数编写 3、默认构造函数 4、内置类型成员的补丁 三、析构函数 1、析构函数概念 2、析构函数编写 3、默认析构函数 四、拷贝构造函数 1、拷贝构造函数概念及编写 2、默认拷贝构造函数 3、拷贝构造…

大学物理·第15章【量子物理】

黑体 斯特藩玻耳兹曼定律 维恩定律 光电效应 在光照射下 &#xff0c;电子从金属表面逸出的现象&#xff0c;叫光电效应. 逸出的电子&#xff0c;叫光电子 经典理论&#xff1a; 光电流值与入射光强成正比截止频率&#xff08;红限&#xff09;v0对某种金属来说&#xff0c;只有…

关于 NodeJs 处理超长字符串问题的分析

问题&#xff1a;对于超大的 string V8不能支持 问题背景 在 Nodejs 计算服务中&#xff0c;对端上上报的内存信息二进制数据进行预处理缓存时&#xff0c;遇到了一个奇怪的报错&#xff1a;RangeError: Invalid string length 。根据该报错信息&#xff0c;查找得知是字符串长…

二叉搜索树(查找,插入,删除)

目录 1.概念 2.性质 3.二叉搜索树的操作 1.查找 2.插入 3.删除(难点) 1.概念 二叉搜索树又称二叉排序树.利用中序遍历它就是一个有顺序的一组数. 2.性质 1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 2.若它的右子树不为空,则右子树上所有节点的值都…

代码解析工具cpg

cpg 是一个跨语言代码属性图解析工具&#xff0c;它目前支持C/C (C17), Java (Java 13)并且对Go, LLVM, python, TypeScript也有支持&#xff0c;在这个项目的根目录下: cpg-core为cpg解析模块的核心功能&#xff0c;主要包括将代码解析为图&#xff0c;core模块只包括对C/C/Ja…

Flink 滚动窗口、滑动窗口详解

1 滚动窗口(Tumbling Windows) 滚动窗口有固定的大小&#xff0c;是一种对数据进行“均匀切片”的划分方式。窗口之间没有重叠&#xff0c;也不会有间隔&#xff0c;是“首尾相接”的状态。如果我们把多个窗口的创建&#xff0c;看作一个窗口的运动&#xff0c;那就好像它在不…

大坝安全监测系统:水库“守坝人”!

一、项目背景 随着社会经济的迅速发展&#xff0c;我国水资源利用率越来越高&#xff0c;各类水利水电工规模进一步扩大。在抗洪救灾、水利发电等方面带来巨大的经济和社会效益。但受多种因素影响&#xff0c;大坝的安全问题日益严重。大量工程实践证明&#xff0c;为保证大坝…

uniapp 离线本地打包

uniapp打包教程地址 https://nativesupport.dcloud.net.cn/AppDocs/usesdk/android.html点击查看 需要的环境&#xff1a; java (1.8)离线SDK(上面的连接下载即可)Android Studio&#xff08;同上&#xff09; 配置环境变量 依次点击“计算机”&#xff0d;“属性”&#…

通过 指针 引用 多维数组 详解

目录 一&#xff1a;回顾多维数组地址知识 二&#xff1a;二维数组的有关指针 三&#xff1a;指向数组元素的指针变量 四&#xff1a;用指向数组的指针作为函数参数 首先简单来讲&#xff0c;指针变量可以指向一维数组中的元素&#xff0c;也可以指向多维数组中的元素。下面…

Java线程中:Runnable和Callable的区别和联系

点个关注&#xff0c;必回关 文章目录一、Java提供了三种创建线程的方法1.继承Thread2.实现Runnable接口3.通过Callable和Future创建线程二、Runnable和Callable的区别和联系1.定义接口&#xff08;1&#xff09; Runnable&#xff08;2&#xff09;Callable&#xff08;3&…

onnx-graphsurgeon----ONNX计算图修改神器

0. 简介 作为深度学习用户&#xff0c;经常会听到ONNX、TensorRT等一系列常用的文件保存格式。而对于ONNX而言&#xff0c;经常我们会发现在利用TensorRT部署到NVIDIA显卡上时&#xff0c;onnx模型的计算图不好修改&#xff0c;在以前的操作中很多时候大佬是将onnx转换成ncnn的…

vscode中安装python运行调试环境

在运行代码之前&#xff0c;需要到微软商店下载安装python环境&#xff0c;35m&#xff0c;都是自动的。 1、安装python 的extensions插件。 ctrlshiftx 输入 python 后点击 install 按钮。 2、新建文件夹spider文件夹。 3、在新建文件夹spider下新建文件spider.py源代码。…

注册OpenAI体验ChatGPT实战演示

什么是ChatGPT ChatGPT&#xff0c;美国OpenAI 研发的聊天机器人程序 &#xff0c;于2022年11月30日发布的人工智能技术驱动的自然语言处理工具&#xff0c;能够真正像人一样完成交流和任务处理。日前&#xff0c;ChatGPT已经更新多个版本&#xff0c;很多大厂也都在接入其API。…

Spark On YARN时指定Python版本

坑很多&#xff0c;直接上兼容性最佳的命令&#xff0c;将python包上传到hdfs或者file:/home/xx/(此处无多余的/) # client 模式 $SPARK_HOME/spark-submit \ --master yarn \ --deploy-mode client \ --num-executors 2 \ --conf "spark.yarn.dist.archives<Python包…

超店有数,tiktok变现 |TikTok最新选品秘籍!让你爆单销量10W+

商家想要自家产品爆单&#xff0c;获得更高的转化&#xff0c;tiktok变现&#xff0c;选品很重要。但很多商家在选品上&#xff0c;找不到头绪。那不妨看看一下的内容&#xff0c;可以帮助你找到选品的思路方向和方法。一、tiktok选品方向1、 考虑垂直细分产品考虑垂直细分领域…