【C++那些事儿】深入理解C++类与对象:从概念到实践(上)| 揭开this指针的神秘面纱

在这里插入图片描述

📷 江池俊: 个人主页
🔥个人专栏: ✅数据结构冒险记 ✅C++那些事儿
🌅 有航道的人,再渺小也不会迷途。


在这里插入图片描述

文章目录

    • 1. 面向过程和面向对象初步认识
    • 2.类的引入
    • 3.类的定义
    • 4.类的访问限定符及封装
      • 4.1 访问限定符
      • 【面试题】
        • 问题:C++中struct和class的区别是什么?
      • 4.2 封装
      • 【面试题】
        • 在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?
    • 5.类的作用域
    • 6.类的实例化
    • 7.类对象模型
      • 7.1 如何计算类对象的大小
      • `问题:类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小?`
      • 7.2 类对象的存储方式猜测
        • 1. 对象中包含类的各个成员
        • 2. 代码只保存一份,在对象中保存存放代码的地址
        • 3. 只保存成员变量,成员函数存放在公共的代码段
      • 7.3 结构体内存对齐规则
    • 8.this指针
      • 8.1 this指针的引出
      • 8.2 this指针的特性
      • 【面试题】
        • 1. this指针存在哪里?
        • 2. this指针可以为空吗?
      • 8.3. C语言和C++实现Stack的对比
        • 1. C语言实现
        • 2. C++实现

1. 面向过程和面向对象初步认识

C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。

在这里插入图片描述
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。

在这里插入图片描述

2.类的引入

C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义函数。

#include<iostream>
#include<stdlib.h>

using namespace std;

typedef int DataType;
struct Stack
{
	// 初始化
	void Init(size_t capacity)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	// 入栈
	void Push(const DataType& data)
	{
		// 扩容
		_array[_size] = data;
		++_size;
	}
	// 取栈顶元素
	DataType Top()
	{
		return _array[_size - 1];
	}
	// 销毁栈
	void Destroy()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
	
	DataType* _array;
	size_t _capacity;
	size_t _size;
};

int main()
{
	Stack s;
	s.Init(10);
	s.Push(1);
	s.Push(2);
	s.Push(3);
	cout << s.Top() << endl;
	s.Destroy();
	return 0;
}

上面结构体的定义,在C++中更喜欢用class来代替
在这里插入图片描述

3.类的定义

class className
{
	// 类体:由成员函数和成员变量组成

};  // 一定要注意后面的分号

class为 定义类的 关键字ClassName为类的名字{}中为类的主体注意 类定义结束时后面 分号不能省略

类体中内容称为 类的成员:类中的 变量 称为类的 属性成员变量; 类中的 函数 称为 类的方法 或者 成员函数

类的两种定义方式

  1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。

在这里插入图片描述
2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
在这里插入图片描述
一般情况下,更期望采用第二种方式

成员变量命名规则的建议:

// 我们看看这个函数,是不是很僵硬?
class Date
{
public:
	void Init(int year)
	{
		// 这里的year到底是成员变量,还是函数形参?
		year = year;
	}
private:
	int year;
};

// 所以一般都建议这样
class Date
{
public:
	void Init(int year)
	{
		_year = year;
	}
private:
	int _year;
};

// 或者这样
class Date
{
public:
	void Init(int year)
	{
		mYear = year;
	}
private:
	int mYear;
};

// 其他方式也可以的。一般都是加个前缀或者后缀标识区分就行。

4.类的访问限定符及封装

4.1 访问限定符

C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用

在这里插入图片描述
【访问限定符说明】

  1. public修饰的成员在类外可以直接被访问
  2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  4. 如果后面没有访问限定符,作用域就到 } 即类结束。
  5. class的默认访问权限为private,struct为public(因为struct要兼容C)

注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

【面试题】

问题:C++中struct和class的区别是什么?

解答:C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和class定义类是一样的,区别是 struct定义的类默认访问权限是public,class定义的类默认访问权限是private
注意:在继承和模板参数列表位置,struct和class也有区别。

4.2 封装

【面试题】

面向对象的三大特性:封装、继承、多态

在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互

封装本质上是一种管理,让用户更方便使用类
比如:对于电脑这样一个复杂的设备,提供给用户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。

在这里插入图片描述
对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此 计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可

在C++语言中实现封装,可以 通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用

5.类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。

class Person
{
public:
	// 成员函数声明
	void PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int  _age;
};

// 这里需要指定PrintPersonInfo是属于Person这个类域(成员函数类外定义)
void Person::PrintPersonInfo()
{
	cout << _name << " " << _gender << " " << _age << endl;
}

6.类的实例化

用类类型创建对象的过程,称为类的实例化

  1. 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;比如:入学时填写的学生信息表,表格就可以看成是一个类,来描述具体学生信息。

  2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量

int main()
{
	Person._age = 100;   // 编译失败:error C2059: 语法错误:“.”
	return 0;
}

Person类是没有空间的,只有Person类实例化出的对象才有具体的年龄

在这里插入图片描述

  1. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间

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

7.类对象模型

7.1 如何计算类对象的大小

class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}
private:
	char _a;
};

问题:类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小?

7.2 类对象的存储方式猜测

1. 对象中包含类的各个成员

在这里插入图片描述
缺陷:每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。那么如何解决呢?

2. 代码只保存一份,在对象中保存存放代码的地址

在这里插入图片描述

3. 只保存成员变量,成员函数存放在公共的代码段

在这里插入图片描述
问题:对于上述三种存储方式,那计算机到底是按照那种方式来存储的?

我们再通过对下面的不同对象分别获取大小来分析看下

// 类中既有成员变量,又有成员函数
class A1 
{
public:
	void f1() {}
private:
	int _a;
};

// 类中仅有成员函数
class A2 
{
public:
	void f2() {}
};

// 类中什么都没有---空类
class A3
{};

int main()
{
	cout << "sizeof(A1) = " << sizeof(A1) << endl;
	cout << "sizeof(A2) = " << sizeof(A2) << endl;
	cout << "sizeof(A3) = " << sizeof(A3) << endl;
	return 0;
}

在这里插入图片描述
结论:一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐 类对象的存储方式是第三种:只保存成员变量,成员函数存放在公共的代码段

注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。

7.3 结构体内存对齐规则

  1. 第一个成员在与结构体偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
    VS中默认的对齐数为8
  3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
    体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

欲知详情请移步:【自定义类型】— 位段、枚举、联合 | 结构体内存对齐 | offsetof宏

8.this指针

8.1 this指针的引出

我们先来定义一个日期类 Date

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;     // 年
	int _month;    // 月
	int _day;      // 日
};
int main()
{
	Date d1, d2;
	d1.Init(2022, 1, 11);
	d2.Init(2022, 1, 12);
	d1.Print();
	d2.Print();
	return 0;
}

对于上述类,有这样的一个问题:

Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?

C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成

8.2 this指针的特性

  1. this指针的类型:类类型*const,即成员函数中,不能给this指针赋值。
  2. 只能在“成员函数”的内部使用。
  3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针
  4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递

在这里插入图片描述

【面试题】

1. this指针存在哪里?
2. this指针可以为空吗?

先来看看两段代码:

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
	void PrintA()
	{
		cout << "Print()" << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->PrintA();
	return 0;
}
// 答案:C、正常运行
// 原因:PrintA函数不在某个实例化的对象中,而是在公共代码区,所以p不会解引用,但是此处的p的作用是作为函数实参传递给函数第一个隐含的this指针。

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->PrintA();
	return 0;
}
// 答案:B、运行崩溃
// 原因:PrintA函数中访问了成员函数_a,而_a的访问需要解引用this指针,但此时的this指针就是p,p是空指针,空指针解引用导致程序运行崩溃

【综上】:

  1. this指针存在于类的非静态成员函数内部。在C++中,每个非静态成员函数都有一个隐含的参数,即this指针。这个指针指向调用该成员函数的对象的地址。通过this指针,成员函数可以访问和修改该对象的成员变量。
  2. this指针可以为空,此外在静态成员函数中,this指针是不存在的。静态成员函数不属于任何对象实例,因此没有this指针。尝试在静态成员函数中访问this指针会导致编译错误。

8.3. C语言和C++实现Stack的对比

1. C语言实现
typedef int DataType;
typedef struct Stack
{
	DataType* array;
	int capacity;
	int size;
}Stack;

void StackInit(Stack* ps)
{
	assert(ps);
	ps->array = (DataType*)malloc(sizeof(DataType) * 3);
	if (NULL == ps->array)
	{
		assert(0);
		return;
	}ps->capacity = 3;
	ps->size = 0;
}

void StackDestroy(Stack* ps)
{
	assert(ps);
	if (ps->array)
	{
		free(ps->array);
		ps->array = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}

void CheckCapacity(Stack* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity * 2;
		DataType* temp = (DataType*)realloc(ps->array,
			newcapacity * sizeof(DataType));
		if (temp == NULL)
		{
			perror("realloc申请空间失败!!!");
			return;
		}
		ps->array = temp;
		ps->capacity = newcapacity;
	}
}

void StackPush(Stack* ps, DataType data)
{
	assert(ps);
	CheckCapacity(ps);
	ps->array[ps->size] = data;
	ps->size++;
}

int StackEmpty(Stack* ps)
{
	assert(ps);
	return 0 == ps->size;
}

void StackPop(Stack* ps)
{
	if (StackEmpty(ps))
		return;
	ps->size--;
}

DataType StackTop(Stack* ps)
{
	assert(!StackEmpty(ps));
	return ps->array[ps->size - 1];
}

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->size;
}

int main()
{
	Stack s;
	StackInit(&s);
	StackPush(&s, 1);
	StackPush(&s, 2);
	StackPush(&s, 3);
	StackPush(&s, 4);
	printf("%d\n", StackTop(&s));
	printf("%d\n", StackSize(&s));
	StackPop(&s);
	StackPop(&s);
	printf("%d\n", StackTop(&s));
	printf("%d\n", StackSize(&s));
	StackDestroy(&s);
	return 0;
}

可以看到,在用C语言实现时,Stack相关操作函数有以下共性:

  1. 每个函数的第一个参数都是Stack*
  2. 函数中必须要对第一个参数检测,因为该参数可能会为NULL
  3. 函数中都是通过Stack*参数操作栈的
  4. 调用时必须传递Stack结构体变量的地址

结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中,即数据和操作数据的方式是分离开的,而且实现上相当复杂一点,涉及到大量指针操作,稍不注意可能就会出错。

2. C++实现
typedef int DataType;
class Stack
{
public:
	void Init()
	{
		_array = (DataType*)malloc(sizeof(DataType) * 3);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}

		_capacity = 3;
		_size = 0;
	}
	
	void Push(DataType data)
	{
		CheckCapacity();
		_array[_size] = data;
		_size++;
	}

	void Pop()
	{
		if (Empty())
			return;
		_size--;
	}

	DataType Top() { return _array[_size - 1]; }
	int Empty() { return 0 == _size; }
	int Size() { return _size; }

	void Destroy()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	void CheckCapacity()
	{
		if (_size == _capacity)
		{
			int newcapacity = _capacity * 2;
			DataType* temp = (DataType*)realloc(_array, newcapacity *sizeof(DataType));
			if (temp == NULL)
			{
				perror("realloc申请空间失败!!!");
				return;
			}
			_array = temp;
			_capacity = newcapacity;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};

int main()
{
	Stack s;
	s.Init();
	s.Push(1);
	s.Push(2);
	s.Push(3);
	s.Push(4);

	printf("%d\n", s.Top());
	printf("%d\n", s.Size());

	s.Pop();
	s.Pop();
	printf("%d\n", s.Top());
	printf("%d\n", s.Size());
	
	s.Destroy();
	return 0;
}

C++中通过类可以将数据 以及 操作数据的方法进行完美结合,通过访问权限可以控制那些方法在类外可以被调用,即封装,在使用时就像使用自己的成员一样,更符合人类对一件事物的认知。而且每个方法不需要传递Stack*的参数了,编译器编译之后该参数会自动还原,即C++中 Stack * 参数是编译器维护的,C语言中需用用户自己维护


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TikTok云手机可以运营多少个账号

在社交媒体平台上&#xff0c;尤其是像TikTok这样的热门应用中&#xff0c;账号运营已经成为了许多人的日常工作。而利用云手机技术&#xff0c;一台手机能够同时运营多个TikTok账号&#xff0c;为用户带来了更大的便利和灵活性。本文将探讨 TikTok云手机能够运营多少个账号&am…

网站的安全防护需要注意哪些问题?有什么方法可以加固网站的防护

网站的安全防护&#xff0c;是一项复杂性、多方面的系统工程。现如今网络安全风险的增加&#xff0c;使得上至国家部门机关&#xff0c;小到个人博客&#xff0c;都有可能遭受网络安全问题。说到网络安全问题&#xff0c;比如&#xff1a;竞争最为激烈的游戏行业&#xff0c;从…

【GO开发工程师】grpc进阶#golang

【GO开发工程师】grpc进阶#golang 推荐个人主页&#xff1a;席万里的个人空间 文章目录 【GO开发工程师】grpc进阶#golang1、protobuf2、grpc2.1、gRPC 的 Metadata机制2.2、grpc拦截器 1、protobuf syntax "proto3"; // 指定使用的 protobuf 版本为 proto3 import…

配置前端项目到 github-pages

Quickstart for GitHub Pages - GitHub Docs

云计算新宠:探索Apache Doris的云原生策略

文章目录 Apache Doris 特性极简架构高效自运维高并发场景支持MPP 执行引擎明细与聚合模型的统一便捷数据接入 Apache Doris 极速 1.0 时代极速列式内存布局向量化的计算框架Cache 亲和度虚函数调用SIMD 指令集 稳定多源 关于 Apache Doris 开源社区基于云原生向量数据库Milvus…

大模型(LLM)的token学习记录-I

文章目录 基本概念什么是token?如何理解token的长度&#xff1f;使用openai tokenizer 观察token的相关信息open ai的模型 token的特点token如何映射到数值&#xff1f;token级操作&#xff1a;精确地操作文本token 设计的局限性 tokenizationtoken 数量对LLM 的影响训练模型参…

设计模式七:责任链模式

文章目录 1、责任链模式2、spring中的责任链模式Spring InterceptorServlet FilterNetty 1、责任链模式 责任链模式为请求创建了一个接收者对象的链&#xff0c;在这种模式下&#xff0c;通常每个节点都包含对另一个节点者的引用。每个节点针对请求&#xff0c;处理自己感兴趣…

基于springboot+vue的大学城水电管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

特征融合篇 | YOLOv8 引入通用高效层聚合网络 GELAN | YOLOv9 新模块

今天的深度学习方法专注于如何设计最合适的目标函数,以使模型的预测结果最接近真实情况。同时,必须设计一个合适的架构,以便为预测提供足够的信息。现有方法忽视了一个事实,即当输入数据经过逐层特征提取和空间转换时,会丢失大量信息。本文将深入探讨数据通过深度网络传输…

UE 贴地绘制/日历/鼠标光标滚轮位置缩放图片/UMG滚动数据从前后添加新UI/多图片批量下载 收费项目源码资源

基本里面的内容本人CSDN发的都有现成代码.里面大部分是功能实现思路.这里面是把这几个功能合成了一个完整5.1项目源码.拿到即用.收费项目源码资源. 1.贴地绘制 2.日历 3.鼠标光标滚轮位置缩放图片 \ 4.UMG滚动数据从前后添加新UI思路 5.多图片批量下载 这是整合的懒人源码包收…

Rocky Linux 运维工具yum

一、yum的简介 ​​yum​是用于在基于RPM包管理系统的包管理工具。用户可以通过 ​yum​来搜索、安装、更新和删除软件包&#xff0c;自动处理依赖关系&#xff0c;方便快捷地管理系统上的软件。 二、yum的参数说明 1、install 用于在系统的上安装一个或多个软件包 2、seach 用…

【HarmonyOS】鸿蒙开发之Video组件——第3.7章

Video组件内VideoOptions属性简介 src&#xff1a;设置视频地址。currentProgressRate&#xff1a;设置视频播放倍速&#xff0c;参数说明如下&#xff1a; number|string&#xff1a;只支持 0.75 &#xff0c; 1.0 &#xff0c; 1.25 &#xff0c; 1.75 &#xff0c; 2.0 。P…

JavaEE进阶(7)Spring Boot 日志(概述、用途、使用:打印日志,框架介绍,SLF4J 框架介绍、更简单的日志输出)

接上次博客&#xff1a;JavaEE进阶&#xff08;6&#xff09;SpringBoot 配置文件&#xff08;作用、格式、properties配置文件说明、yml配置文件说明、验证码案例&#xff09;-CSDN博客 目录 日志概述 日志的用途 日志使用 打印日志 在程序中获取日志对象 使用日志对象…

androidble蓝牙开发,12道Android高级面试题

Java面试题 1.GC机制 垃圾回收需要完成两件事&#xff1a;找到垃圾&#xff0c;回收垃圾。 找到垃圾一般的话有两种方法&#xff1a; 引用计数法&#xff1a; 当一个对象被引用时&#xff0c;它的引用计数器会加一&#xff0c;垃圾回收时会清理掉引用计数为0的对象。但这种方…

论文笔记:A survey on zero knowledge range proofs and applications

https://link.springer.com/article/10.1007/s42452-019-0989-z 描述了构建零知识区间证明&#xff08;ZKRP&#xff09;的不同策略&#xff0c;例如2001年Boudot提出的方案&#xff1b;2008年Camenisch等人提出的方案&#xff1b;以及2017年提出的Bulletproofs。 Introducti…

SpringBoot案例(黑马学习笔记)

这个案例呢&#xff0c;就是Tlias智能学习辅助系统。 参考接口文档完成后端功能的开 发&#xff0c;然后结合前端工程进行联调测试即可。 完成后的成品效果展示&#xff1a; 准备工作 需求&环境搭建 需求说明 部门管理 部门管理功能开发包括&#xff1a; ● 查询部门列…

【JavaSE】实用类——枚举类型、包装类、数学类

目录 Java API枚举优势代码示例 包装类作用包装类和基本数据类型的对应关系包装类的构造方法包装类的常用方法装箱和拆箱 留一个问题大家猜猜看包装类的特点 Math类Random类代码示例 Java API Java API(Java Application Programming Interface) 即Java应用程序编程接口&#…

设备管理系统解决方案

软件资料获取&#xff1a;软件项目开发全套文档下载_软件项目文档-CSDN博客 1.系统概述 1.1.需求描述 建立设备信息库&#xff0c;对设备相关档案的登录、整理。通过建立完善的设备档案&#xff0c;将设备的各类原始信息进行信息化管理&#xff0c;使设备档案查询工作方便快…

NoReturn与None的区别

问题来源&#xff1a; class xx:def __init__(self) -> NoReturn:passpycharm编译器提示如下问题&#xff1a; init should return None 根因探索&#xff1a; None 是Python的一个特殊的数据类型&#xff0c;用于表示空值或者没有值。当一个函数没有显式返回值时&#x…

模型预测控制MPC算法的讲解-案例(C语言代码)

目录 一、模型预测控制MPC的基本原理 1.1 建立模型 1.2 设定目标和约束条件 1.3 求解优化问题 1.4 应用控制输入 1.5 重复优化 二、模型预测控制MPC的特点 三、应用场景 四、应用案例 一个MPC算法的简化版框架&#xff1a; 4.1 案例系统模型 4.2 控制目标和当前状态…