【C++初阶】类和对象下篇

news2024/11/25 4:19:21

⭐博客主页:️CS semi主页
⭐欢迎关注:点赞收藏+留言
⭐系列专栏:C++初阶
⭐代码仓库:C++初阶
家人们更新不易,你们的点赞和关注对我而言十分重要,友友们麻烦多多点赞+关注,你们的支持是我创作最大的动力,欢迎友友们私信提问,家人们不要忘记点赞收藏+关注哦!!!

类和对象下篇

  • 前言
  • 一、再谈构造函数
    • 1、构造函数体赋值
    • 2、初始化列表
      • (1)为何要设计初始化列表
      • (2)初始化列表的语法和构造
    • 3、三种必须放在初始化列表位置进行初始化
    • 4、尽量使用初始化列表
    • 5、初始化顺序
    • 6、隐式类型转换
    • 7、explicit关键字
  • 二、static成员
    • 1、概念
    • 2、全局类对象
    • 3、封装到类
    • 4、特性
    • 5、小问题
  • 三、友元
    • 1、友元函数
    • 2、友元类
  • 四、内部类
    • 1、概念
    • 2、sizeof外部类
  • 五、匿名对象
  • 六、再次理解类和对象
  • 总结


前言

我们进入最后一个章节,类和对象的下篇,这里我们会分六个板块进行讲解,每一个板块都是深入浅出,直接出发!


一、再谈构造函数

1、构造函数体赋值

如下是我们经常写的构造函数:
在这里插入图片描述
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值

2、初始化列表

(1)为何要设计初始化列表

1、有些情况下靠着构造函数赋值是不能够满足条件的,所以就创出来了初始化列表来弥补构造函数赋值不足的情况,因为每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)。
2、有些成员必须在初始化列表中初始化,这个后续再谈。

(2)初始化列表的语法和构造

一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式
在这里插入图片描述

3、三种必须放在初始化列表位置进行初始化

1、引用成员变量
2、const成员变量
3、自定义类型成员(且该类没有默认构造函数时)

我们先来细细聊一聊2和3这两个特征,const成员变量和自定义类型成员的特征是在定义的时候初始化,我们知道,在函数体内叫赋值,而在定义时候初始化就需要用到初始化列表。

在这里插入图片描述

我们再来细细聊一下我们的1:
首先我们需要先明确的一个概念就是什么是默认构造函数,很简单,默认构造函数就是无参的/带参并有初始化赋值的

在这里插入图片描述

4、尽量使用初始化列表

尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。

我们讲尽量使用初始化列表,那么我们就举初始化列表不能进行方便的例子。

//动态开辟一个二维数组
class A
{
public:
	A(int row = 10, int col = 5)
		:_row(row)
		,_col(col)
	{
		_a = (int**)malloc(sizeof(int*) * row);
		if (_a == nullptr)
		{
			perror("malloc fail\n");
			exit(-1);
		}
		for (int i = 0; i < row; i++)
		{
			_a[i] = (int*)malloc(sizeof(int) * col);
		}
	}
private:
	int** _a;
	int _row;
	int _col;
};

这个例子我们的初始化列表是无法写动态开辟的,因为太长了,所以我们要灵活运用这些知识,尽可能使用初始化列表。

5、初始化顺序

成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

在这里插入图片描述
在这里插入图片描述
我们答案选择D,解析:

是按照类中声明的次序的,所以先赋值的是第二个,而第二个的_a1并没有进行赋值和初始化,所以为随机值,我们称之为“野引用”。

6、隐式类型转换

在这里插入图片描述
我们可以看一下是否调用了拷贝构造:
在这里插入图片描述
所以编译器会进行优化,将原本需要构造和拷贝进行合一成仅需构造,因为会有个临时变量,先构造出再进行拷贝,现在编译器直接合二为一了,直接一步构造函数即可。

7、explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。

我们使用explicit关键字可以将原本需要进入构造函数的不让它进入,explicit修饰构造函数,禁止类型转换。
同样explicit关键字同样可以禁止构造函数的隐式类型转换。

在这里插入图片描述
在这里插入图片描述

二、static成员

1、概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化

2、全局类对象

int _scount = 0;

class A
{
public:
	A() { ++_scount; }
	A(const A& t) { ++_scount; }
	~A() { --_scount; }
	//static int GetACount() { return _scount; }
private:
	//static int _scount;
};

void Func()
{
	static A a2;
	cout << __LINE__ << ":" << _scount << endl;

}

A a;

int main()
{
	A a1;
	cout << __LINE__ << ":" << _scount << endl;
	Func();
	cout << __LINE__ << ":" << _scount << endl;

	return 0;
}

大家算一算最终输出的是什么?
答案揭晓:2 3 3。
其实很简单,先定义了一个全局的对象和一个main函数局部的对象,这样子是先调用两次构造函数,而Func函数中的静态变量始终占一个构造函数,因为出了Func函数作用域其不会消失,存放在静态区。所以是2 3 3。

可是我们在main函数中将_scount进行++发现最终输出的结果会发生改变,那么全局变量的劣势就显现出来了,它可以在任意地方进行更改,那么对于整个函数的影响是很大的。

3、封装到类

class A
{
public:
	A() { ++_scount; }
	A(const A& t) { ++_scount; }
	~A() { --_scount; }
	// 静态成员函数,没有this指针,指定类域和访问限定符就可以访问
	static int GetCount() { return _scount; }
private:
	// 成员变量 -- 属于每一个类对象,存储对象里面
	int _a = 1;
	int _b = 2;

	// 静态成员变量 -- 属于类,属于类的每个对象共享,存储在静态区
	static int _scount;
};

//类外面定义
int A::_scount = 0;

4、特性

  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

5、小问题

  1. 静态成员函数可以调用非静态成员函数吗?
  2. 非静态成员函数可以调用类的静态成员函数吗?

对于1来讲是不可以的,因为静态成员函数没有this指针,非静态的并不能调过去。
对于2来讲是可以的,因为用不到this指针。

三、友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元分为:友元函数和友元类

1、友元函数

我们在之前讲到的重载operator<<和operator>>两个函数用到的是友元函数,是将函数体的定义放到全局函数中,没有this指针则两个形参可以随意颠倒,然后友元突破私有的限制在全局的函数体中进行使用。

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
	_cin >> d._year;
	_cin >> d._month;
	_cin >> d._day;
	return _cin;
}

说明:
友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同

2、友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
1、友元关系是单向的,不具有交换性。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
2、友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元。

四、内部类

1、概念

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的天然友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

特性

  1. 内部类可以定义在外部类的public、protected、private都是可以的,私有的别人不能用,公有可以用。
  2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  3. sizeof(外部类)=外部类,和内部类没有任何关系。

2、sizeof外部类

在这里插入图片描述
大家算一算sizeof(A)为多少?其实是4。跟静态的和另一个B类都无关,我们可以这样理解,A是外部类,是一个大图纸,B是内部类,是A的图纸中夹了一层图纸,这两个图纸直接是没有什么联系的,只不过都是图纸所以算A类的字节大小就单纯算A的。那当然了,不能单纯拿到B类的图纸。

五、匿名对象

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a = 0)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};

class Solution
{
public:
	int Sum_Solution(int n) {
		cout << "Sum_Solution" << endl;
		//...
		return n;
	}
};

int main()
{
	A aa(1); //有名对象 -- 生命周期在当前函数局部域
	A(2); //匿名对象 -- 生命周期在当前行

	Solution().Sum_Solution(10); //匿名对象 -- 生命周期在当前行
	
	const A& ra = A(1);//延长生命周期

	return 0;
}

匿名对象具有常性,生命周期只在当前行,而const引用延长了匿名对象的生命周期,生命周期在当前函数局部域中。

class A
{
public:
	A(int a = 0)
	: _a(a)
		{
		cout << "A(int a)" << endl;
	}
	
	A(const A & aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}
	
	A & operator=(const A & aa)
	{
		cout << "A& operator=(const A& aa)" << endl;
		
		if (this != &aa)
		{
			_a = aa._a;
		}
		
		return *this;
	}
	
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};

void Func1(A aa)
{}

A Func5()
{
	A aa;
	return aa;
}

int main()
{
	A ra1 = Func5(); // 拷贝构造+拷贝构造 ->优化为拷贝构造
	cout << "==============" << endl;
	A ra2;
	ra2 = Func5();
	
	//A aa1;
	//Func1(aa1); // 不会优化
	
	//Func1(A(1)); // 构造+拷贝构造 ->优化为构造
	//Func1(1);    // 构造+拷贝构造 ->优化为构造
	
	//A aa2 = 1;  // 构造+拷贝构造 ->优化为构造
	
	
	return 0;
}

六、再次理解类和对象

现实生活中的实体计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识。比如想要让计算机认识洗衣机,就需要:

  1. 用户先要对现实中洗衣机实体进行抽象—即在人为思想层面对洗衣机进行认识,洗衣机有什么属性,有那些功能,即对洗衣机进行抽象认知的一个过程
  2. 经过1之后,在人的头脑中已经对洗衣机有了一个清醒的认识,只不过此时计算机还不清楚,想要让计算机识别人想象中的洗衣机,就需要人通过某种面相对象的语言(比如:C++、Java、Python等)将洗衣机用类来进行描述,并输入到计算机中
  3. 经过2之后,在计算机中就有了一个洗衣机类,但是洗衣机类只是站在计算机的角度对洗衣机对象进行描述的,通过洗衣机类,可以实例化出一个个具体的洗衣机对象,此时计算机才能洗衣机是什么东西。
  4. 用户就可以借助计算机中洗衣机对象,来模拟现实中的洗衣机实体了。

总结

类和对象就此告一段落,但是我们是需要不断学习新的知识的,本章节类和对象讲解了什么是类 – 封装在class中,什么是对象 – 我们的图纸的概念,什么是对象实例化 – 我们依靠这个图纸进行创造一个实例化的东西,那当然有诸多细节,是需要不断归纳总结的。


家人们不要忘记点赞收藏+关注哦!!!

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

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

相关文章

板材激光切割机切割穿孔时注意的几个问题

激光切割设备广泛应用于钣金、五金制品、钢结构、汽车配件、广告、工艺品等行业&#xff0c;成为加工行业不可缺少的环节。在厚板加工中穿孔时间占很大比重&#xff0c;随着加工板材越来越厚&#xff0c;板材激光切割机切割穿孔也会相应地增加难度。 激光切割机两种常见的穿孔方…

druid 远程命令执行 (CVE-2021-25646)

漏洞原理 该漏洞主要就是根据Jackson解析特性(解析name为""时)会将value值绑定到对象(JavaScriptDimFilter&#xff0c;type为javascript时指定的)的对应参数(config)上&#xff0c;造成JavaScriptDimFilter中function属性中的javascript代码被执行。攻击者可以构造…

Redis高级数据结构HyperLogLog

HyperLogLog(Hyper[ˈhaɪpə(r)])并不是一种新的数据结构(实际类型为字符串类型)&#xff0c;而是一种基数算法,通过HyperLogLog可以利用极小的内存空间完成独立总数的统计&#xff0c;数据集可以是IP、Email、ID等。 如果你负责开发维护一个大型的网站&#xff0c;有一天产品…

pytorch移植华为mindspore记录

因为某个需求&#xff0c;需要把原来pytorch的神经网络移植到华为的mindspore上 这边记录下遇到的坑 附上mindspore的官方教程&#xff1a; https://mindspore.cn/tutorials/zh-CN/r2.0/advanced/compute_graph.html 这边附上需要移植的网络&#xff0c;以tensorflow和pytorch…

LeetCode 链表OJ分享

目录 删除排序链表中的重复元素回文链表剑指Offer 06.从尾到头打印链表复制带随机指针的链表 删除排序链表中的重复元素 链接: link 题目描述&#xff1a; 题目思路&#xff1a; 本题思路使用双指针&#xff0c;以示例二为例如下图&#xff1a; 如果head->val等于next-&…

ihateniggers:针对Python开发者的Windows远控木马分析

背景 墨菲安全实验室在持续监测开源软件仓库中的投毒行为&#xff0c;5 月 9 日起发现 4 个包含 “ihateniggers” 远程控制木马的 Python 包被 nagogygmail.com 邮箱关联的账号发布到 PyPI 仓库&#xff0c;试图针对Windows系统下 Python 开发者进行攻击。木马利用了discord、…

各种顺序表和链表的实现代码

目录 一、什么是线性表 二、顺序表 2.1什么是顺序表 2.2静态顺序表的代码实现 2.3动态顺序表的代码实现 三、链表 3.1什么是链表 3.2不带头单向不循环链表的代码实现 3.3带头双向循环链表的代码实现 四、顺序表和链表的区别 一、什么是线性表 线性表是n个具有相同特性…

(十五)数据编辑——图形编辑①

数据编辑——图形编辑① 数据编辑包括几何数据和属性数据的编辑。几何数据的编辑主要是针对图形的操作&#xff0c;即图形编辑&#xff0c;包括平行线复制、缓冲区生成、镜面反射、图层合并、结点操作、拓扑编辑等。属性编辑主要包括图层要素属性的添加、删除、修改、复制、粘…

谷歌落子,我们对中国大模型的期待应该是什么?

对中国大模型厂商而言&#xff0c;市场期待的&#xff0c;也恰是这些真正可落地的应用和实践。这些实践可以在社交&#xff0c;在电商&#xff0c;在低代码&#xff0c;在供应链&#xff0c;也更可以在一个个中国产业数字化转型的新洼地。 作者|思杭 皮爷 出品|产业家 在微软G…

裸辞5个月,面试了37家公司,终于.....

上半年裁员&#xff0c;下半年裸辞&#xff0c;有不少人高呼裸辞后躺平真的好快乐&#xff01;但也有很多人&#xff0c;裸辞后的生活五味杂陈。 面试37次终于找到心仪工作 因为工作压力大、领导PUA等各种原因&#xff0c;今年2月下旬我从一家互联网小厂裸辞&#xff0c;没想…

执行增删改查时的结果处理

查询最终走到PreparedStatementHandler类的query方法&#xff0c;执行查询后调用DefaultResultSetHandler类的handleResultSets方法 1.处理返回的普通实体类 DefaultResultSetHandler类的handleResultSets方法 继续本类的handleResultSet方法 通过 handleRowValues 方法来…

设计一个可靠的自动化测试框架需要考虑哪些问题呢?

随着软件开发的日益普及&#xff0c;自动化测试框架逐渐成为了保障软件质量的必备工具。然而&#xff0c;如何设计一个可靠的自动化测试框架并不是一件简单的事情&#xff0c;需要考虑多方面的问题。本文将从需求分析、架构设计、测试用例编写等多个角度&#xff0c;介绍设计一…

【SpringMVC】| SpringMVC拦截器

目录 一&#xff1a;SpringMVC拦截器 1. 拦截器介绍 2. HandlerInterceptor接口分析 3. 自定义拦截器实现权限验证 一&#xff1a;SpringMVC拦截器 SpringMVC 中的 Interceptor 拦截器&#xff0c;它的主要作用是拦截指定的用户请求&#xff0c;并进行相应的预处理与后处理…

C++面向对象(黑马程序员)

内存分区模型 #include<iostream> using namespace std;//栈区数据注意事项&#xff1a;不要返回局部变量的地址 //栈区的数据由编译器管理开辟和释放int* func(int b) //形参数据也会放在栈区 {b 100;int a 10; //局部变量存放在栈区&#xff0c;栈区的数据在函数执…

Qt创建项目 No suitable kits found.

最近因为要写.net6项目&#xff0c;而更新了Visual Studio&#xff0c;后来再使用Qt的时候发现不能编译了。 调整后&#xff0c;发现新建项目显示“No suitable kits found”错误。 网上也看了很多的解决方案&#xff0c;但都不得要领&#xff0c;也都没有解决。 ----------…

Java-Redis持久化之AOF操作

Java-Redis持久化之AOF操作 1.AOF(append only file)2.请你用自己的话说一说 AOF的过程?3.配置如何开启AOF操作4.AOF的优点和缺点: 1.AOF(append only file) 将我们所有的命令都记录下来保存到文件&#xff0c;恢复的时候把这个文件都执行一遍。 2.请你用自己的话说一说 AOF…

【Vue】学习笔记-全局事件总线

全局事件总线(GlobalEventBus) 一种可以在任意组件通信的方式&#xff0c;本质上就是一个对象&#xff0c;它必须满足以下条件 所有的组件对象都必须能看见他这个对象必须能够使用$ on $ emit $ off方法取绑定、触发和解绑事件 使用步骤 定义全局事件总线 //创建VUE new V…

WPF中的用户控件和自定义控件详解

总目录 文章目录 总目录一、什么是用户控件和自定义控件1 用户控件和自定义控件2 相关知识点2 用户控件和自定义控件的适用场景 二、用户控件的使用1.通过依赖属性和路由事件封装用户控件的案例2.通过依赖属性命令依赖属性封装用户控件的案例3.只对外提供属性不对外提供操作的用…

USBCAN卡和台达AS228T通信

AS228R 如图 A.把CAN卡固定上 B.接一个120欧的终端电阻 C.把来可的USBCAN卡的CANH和CANL分别接入AS228R的CANH和CANL. CAN 接口及网络拓扑 10.2.4.1 CAN 网络实体信号的定义及数据格式 CAN 信号为差分信号&#xff0c;信号电压为 CAN和 CAN-之间的电压差&#xff0c;CAN 和…

怎样删除hao123(浏览器首页被篡改了)

有时候我们打开浏览器发现首页被hao123 ,或者2345 这些浏览器给篡改了 或者打开的时候直接打开2个.这个时候想要删除它们,其他它们本身就是网页的,没有应用 在卸载的地方就不用了,它们就嵌套你的浏览器里面,打开的时候启动了他们, 下面说下方法 1 查看浏览器在什么方法下载…