【C++深入学习】类和对象(一)

news2024/9/21 10:43:00

在这里插入图片描述

欢迎来到Harper·Lee的学习笔记!
博主主页传送门:Harper·Lee博客主页!
欢迎各位大佬交流学习!


在这里插入图片描述

本篇本章正式进入C++的类和对象部分,本部分知识分为三小节。
复习:

  1. 结构体复习–内存对齐
  2. 编译和链接
  3. 函数栈桢的创建和销毁

一、面向过程与面向对象

1.1 面向过程

在之前学习的C语言是一种面向过程的语言,它关注的是求解问题的具体实现过程,一般通过函数调用逐步解决问题。就比如说我们以面向过程的方式分析淘米:
QQ截图20240713110621.png

1.2 面向对象

C++就是一种面向对象的语言,它将一个问题分成多个对象,更强调对象与对象之间的联系。在淘米煮饭这个例子中,一共有四个对象:米、水、锅、人。
整个过程中,这四个对象之间是交互完成任务的。在面向对象中,我们更强调对象之间的连续,并不太在意其内在是如何完成的。
QQ截图20240713110653.png

二、类的定义(class/struct)

2.1 类定义格式

  1. class是定义类的关键字,Stack是类的名称,{ }中是类的主体,注意类定义结束时后面的分号不能省略。类中的内容叫做类的成员,包括:类的属性或成员变量;类中的函数称为类的方法或成员函数。C语言中的结构体是类和函数分离的,但是C++不是。
#include<iostream>
using namespace std;
class className//类的定义方式1——class
{
	//成员函数
	void Push(int x);
	void Pop();
	int Top();

	//成员变量
	int* a;//数组
	int top;
	int capacity;
};
  1. C++将C语言中的结构体升级为类。C语言中结构体只能定义变量,在C++中,类不仅可以定义变量,还可以定义函数。
    | struct 在C/C++中的用法不同点 | C语言 | C++ |
    | — | — | — |
    |
    1. 可以定义的内容
    | struct 只能定义变量 | struct可以定义变量、函数 |
    |
    2. 类型
    | struct +结构体名 | 类名 |
    |
    3. 定义变量的方法
    | struct +结构体名才可以定义变量 | 直接类名即可定义,不需要struct 关键字,也可以写(旧法) |
    |
    4. typedef
    | 通常用typedef使类型名称简单些 | 直接类名就可以用 |
    |
    5. 能否定义类
    | C语言中struct 可以定义类 | C++中struct 可以定义类 |
//test.c
//在C语言中,一般都会使用typedef
struct Node1//结构体
{
	struct Node1* next;//注意这里:struct Node1*才是类型名,才能用它定义变量next
	int val;
};

//test.cpp
#include<iostream>
using namespace std;
struct Node2//类的定义方式2——struct
{
	Node2* next;//区别改变在这里,直接用类名,
	int val;
    void top();
};//这一中在C语言中是不能通过的!!!!C++兼容C语言,C语言不兼容C++!!!
int main()
{
    //test.cpp
    Node2 n1;//定义n1
    struct Node2 n2;//定义n2,C语言的用法C++也支持!
    
    return 0;
}
  1. 定义在类里面的函数默认为 inline 内联的。

也可以进行声明和定义的分离(比如声明和定义在不同文件中)
**a. 声明和定义没有分离。**注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理

//1. 声明和定义没有分离:
#include<iostream>
using namespace std;
class Date
{
public:
	void Init(int year, int month, int day)
    {
    	_year = year;
    	month = month;
    	day = day;
    }

private:
	int _year;
	int _month;
	int _day;
};

b.声明和定义分离。声明放在头文件(.h)中,定义放在源文件(.cpp)中。 声明和定义分离需要指定类域,就使用了::域作用限定符。
QQ截图20240713110724.png

//test.h
#include<iostream>
using namespace std;
class Date
{
public:
	void Init(int year, int month, int day);//声明
private:
	int year;
	int month;
	int day;
};

//test.cpp
void Date::Init(int year, int month, int day)//定义
                                             //声明和定义分离需要指定类域,就使用了域作用限定符
{			                                 //Init是类Date的成员函数,只是声明和定义分离了而已
	year = year;
	month = month;
	day = day;
}

2.2 类的访问限定符

在C++中有三种访问限定符:public、private、protected
QQ截图20240713110744.png

  1. public修饰的成语言在类外可以直接被访问;
  2. protected和private修饰的成员在类外不能直接被访问;
  3. 访问权限作用域从该访问限定符出现的位置开始一直到下一个访问限定符出现为止,如果后面没有访问限定符,作用域就到 } 即类结束;
  4. class的默认访问限定符是private,struct的默认访问限定符是public。(因为struct要兼容C)
  5. ⼀般成员变量都会被限制为private/protected,需要给别人使用的成员函数会放为public。protected一般在继承中比较常用。
#include<iostream>
using namespace std;
class Date
{
public://可以访问
	void Init(int year, int month, int day)//非成员变量
	{
		_year = year;
		_month = month;
		_day = day;
	}
private://不可以访问
	int _year;//成员变量
	int _month;
	int _day;
};

2.3 成员变量与非成员变量的区分

从下面的代码可以看出,类中的成员变量和非成员变量之间存在着重名冲突。

#include<iostream>
using namespace std;
class Date
{
public:
	void Init(int year, int month, int day)//非成员变量
	{
		year = year;
		month = month;
		day = day;
	}
private:
	int year;//成员变量
	int month;
	int day;
};

C++标准并没有规定成员变量的命名规则。为了区分出成员变量,惯例上,我们在定义成员变量时会对其进行特定地修饰,比如_变量名或者是m_变量名或者变量名_。 不同公司可能都有一套自己的命名规则,但目的只是为了区分出成员变量。

#include<iostream>
using namespace std;
class Date
{
public:
	void Init(int year, int month, int day)//非成员变量
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;//成员变量
	int _month;
	int _day;
};

2.4 变量声明和定义的区别

  1. 变量的声明:告知变量的名称、类型,没有开辟空间;

  2. 变量的定义:开辟了空间。如果是定义,就有空间,就可以直接使用/访问。如下面main函数中的_year。

#include<iostream>
using namespace std;
class Date
{
public:
	void Init(int year, int month, int day);//声明
private:
	int _year;//是定义还是声明????————声明
	int _month;
	int _day;
};

//test.cpp
void Date::Init(int year, int month, int day)//定义
{			                              
	_year = year;
	_month = month;
	_day = day;
}
int main()
{
	Date::_year = 2024;//error,所以不能直接使用变量,说明没有开辟空间,_year是声明
	return 0;
}

三、类的作用域

3.1 类域的基本特点

  1. 同一个域里面不能定义同名变量,但是不同的域里面可以定义重名变量。
  2. 类域和命名空间域只影响名字隔离,不影响各自的生命周期
  3. 类域影响的是编译的查找规则, 查找默认在局部和空间去找,不会在命名空间域中去查找,除非使用== ::限定符 ==指定了类域。

3.2 编译器的查找规则

  1. (图源于博主:vpurple__)

屏幕截图 2024-07-04 172820.png


屏幕截.png

四、类对象模型

4.1 类对象的实例化

类:是对对象进行描述的、就像模型一样的东西。
类限定了类有哪些成员,类中的成员变量是一种声明,并没有分配实际的内存空间来存储它。就比如我们可以根据一张图纸(没有实际空间),创建出好几座不一样的房屋(有实际空间)。二者不一样的房子就是类实例化出的对象。
QQ截图20240713110821.png
用类名定义对象的方式,称为类的实例化。单独的类并不占据实际空间。而且一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类的成员变量。

#include<iostream>
using namespace std;

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()
{
    //实例化出d1,d2两个对象
	Date d1;//Date类名就是一个类型,直接用来定义
	Date d2;

	d1.Init(2024, 7, 10);/
	d1.Print();
	d2.Init(2024, 1, 1);
	d2.Print();

	return 0;
}

4.2 类对象的存储

4.2.1 成员变量和成员函数的存储???

#include<iostream>
using namespace std;

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;
	Date d2;

	d1.Init(2024, 7, 10);//使用 . 操作符访问成员对象
	d1.Print();

	d2.Init(2024, 1, 1);
	d2.Print();

	return 0;
}

上面的代码,透过汇编观察可以发现,类定义的不同对象虽然成员变量不同,就需要各自存储他们;但是调用的成员函数其实是一样的(地址是一样的),如果在每个对象里面都存储一份,就会有空间的重复浪费。(例如:用类实例化出100个对象,每个成员函数都要重复地存储100次,实属浪费!)
QQ截图20240713110850.png
其实,函数指针是不需要存储的。函数指针是一个地址,调用函数被汇编成汇编指令[call地址、jmp指令]。编译器在编译链接时就要找到函数的地址,不是在运行时找;而动态多态是在运行时找的,就需要存储函数地址。——跳转到下面的this指针
总结:

  1. 类定义出的不同对象是各自存储成员变量的。
  2. 通过汇编发现,成员函数是在编译链接时找到函数地址,函数地址存储在一个公共的地方,然后编译成汇编指令[如call地址、jmp指令等]的。
  3. 对象中只存储成员变量,而且函数体中并没有关于不同对象的区分,那么不同对象在调用成员函数Init、Print时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?——这里就引出了this指针!!!(先跳转学习this指针!)

4.3 类对象的大小

4.3.1 一般类的计算

image.pngimage.png
C++的内存对齐规则和C语言结构体中的内存对齐规则一模一样!
· 第一个成员在与结构体偏移量为0的地址处;
· 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处;
· 注意:对齐数=编译器默认的一个对齐数与该成员大小的较小值;
· VS中默认的对齐数为8;
· 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍;
• 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小;
·就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍;
C语言:自定义类型——结构体(✿༺小陈在拼命༻✿)(这一部分知识可以参考这位优秀博主)
计算A实例化对象的大小:

#include<iostream>
using namespace std;
class A
{
public:
	void Print()
	{
		cout << _ch << endl;
	}
private:
	char _ch;
	int _i;
};
int main()
{
	A a;

	cout << sizeof(a) << endl;
	cout << &a << endl;
	return 0;
}

运行结果:
image.png
画图分析:
QQ截图20240713110919.png

Q:为什么要内存对齐?
A:CPU读取内存数据时,并不是从任意位置开始读取的,读取与CPU的字长、数据总线有关,
如果CPU可以从任一位置读取,那么内存对齐规则不仅麻烦,还浪费时间。为什么还要有对齐规则呢?
读取数据与数据总线有关,32根数据总线(4Byte,数据总线数量与机器型号有关)----计算机组成原理。规定:从起始点开始,不管数据有多少,CPU一次都读取32个数据,但是这里面也许只有1个数据是CPU真正想读取的。
综合来看,内存对齐规则减少访问次数、提高了CPU的效率。

4.3.2 空类的计算

空类:类中只有成员函数或者什么也没有时,这个类就叫做空类。

//计算⼀下B/C实例化的对象是多⼤?
#include<iostream>
using namespace std;
class B
{
public:
	void Print()
	{
		//...
	}
};
class C
{};
int main()
{
	B b;
	C c;
    
	cout << sizeof(b) << endl;
	cout << &b << endl;
    
	cout << sizeof(c) << endl;
	cout << &c << endl;

	return 0;
}

运行结果:
image.png
B和C都是空类。首先类对象的大小不能为0,否则不能表示该对象存在过,而且还要通过B对象开空间去实例化呢。因此,C++给空类1Byte的空间用来占位标记这个类的对象的存在,实际操作中的使用性也很少。

五、this指针

5.1 this指针的引入

先观察这段代码:

#include<iostream>
using namespace std;

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;
	Date d2;

	d1.Init(2024, 7, 10);//使用 . 操作符访问成员对象
	d1.Print();
	d2.Init(2024, 1, 1);
	d2.Print();

	return 0;
}

运行结果:
image.png
根据前面 3.2.1 的分析可知,类中的成员函数并没有存储在类的对象中,而且函数体中并没有关于不同对象的区分,那么函数是如何知晓自己应该访问哪个对象的呢?
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数”增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象)(因此,this指针是一个当前类类型的指针,传参就要传入&类名定义的对象名,如 &d1)。
在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。这个隐藏的指针参数就是this指针。
类的成员函数中访问成员变量,本质都是通过this指针来访问的(编译时编译器自动会处理)。如Init函数中给_year 赋值:this -> _year = year;
加入this指针后的原型代码如下面的注释:

#include<iostream>
using namespace std;
class Date
{
public:
	//原型:void Init(Date* const this, int year, int month, int day)
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//原型:void Print(Date* const this)//const修饰的是this
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
		//原型可以显示写:cout << this -> _year << "-" <<  this -> _month << "-" << this -> _day << endl;
        
	}
private:
	int _year;
	int _month;
	int _day;
    //this->_year = year;原型
    //this->_month = month;
    //this->_day = day;//这个this->可以显示地写出来,因为调用函数体中的this指针
};
int main()
{
	Date d1;
	Date d2;

	d1.Init(2024, 7, 10);//原型:d1.Init(&d1, 2024, 7, 10);
	d1.Print();			 //原型:d1.Print(&d1);

	d2.Init(2024, 1, 1); //原型:d2.Init(&d2,2024,1,1);
	d2.Print();			 //原型:d2.Print(&d2);

	return 0;
}

5.2 this指针的特点

  1. this指针的类型:this指针是一个当前类类型的指针, const ,所以成员函数中,不能给this指针赋值,不能修改this,可以修改this指针指向的内容的;*
  2. this指针本质上是成员函数第一个隐含的指针形参(所以this指针存储在函数栈桢中),当对象调用成员函数时,将对象地址作为实参传递给this形参,所以对象中不存储this指针;(之前计算对象实例化大小的时候都没有计算this指针的大小)
  3. this指针是指针形参,一般情况下都由编译器通过ecx寄存器自动传递,不需要用户和传递;(VS编译器)

image.png

  1. C++规定不能在实参和形参的位置显示的写this指针(编译时编译器会处理),但是可以在函数体内显示使用this指针。(如上面的代码例子)

5.3 Question

5.3.1 Q1

  1. 下面程序编译运行结果是(C.)
  • A.编译报错
  • B. 运行崩溃
  • C. 正常运行
#include<iostream>
using namespace std;
class A
{
public:
	void Print()
	{
        //cout << this << endl;//输出空指针0000000,不会报错,因为没有使用!!
		cout << "A::Print()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;//p是一个nullptr指针空值,类型为 A* ,是类类型
               //mov ecx p
	p->Print();//call 地址//p->Print(p);//这里不需要取地址!!!
    //p -> _a = 1;//error这个才是空指针解引用,_a存储在对象里面的(_a是public时)
	return 0;
}

运行结果:image.pngimage.png???
image.png
**程序正常运行,**分析过程:

  1. 编译器被编译后,底层都是将其转换成汇编指令。p->Print(); 转化的底层汇编是 call 地址,因为成员函数的地址指针不存储在对象里,该地址不在 p 对象里,编译成汇编指令后在符号表里面。
  2. 调用函数需要传递参数,参数传递 this 指针,this是类类型的一个指针,这里不需要取地址,因为 p (实参,它已经是地址了,不用取地址!)就是类类型指针,传递给行参,但是并没有使用该空指针,因此没有空指针报错!p->Print(); 并不是空指针解引用!!
  3. 成员函数并不存放在类对象中,而是存放在公共代码段。虽然我们表面看上去解引用,但实际上编译器不需要通过解引用去找对应函数,只需要去公共代码区执行对应函数即可。因此并不是空指针解引用!

5.3.2 Q2

  1. 下面程序编译运行结果是(B.)
  • A.编译报错
  • B. 运行崩溃
  • C. 正常运行
#include<iostream>
using namespace std;
class A
{
public:
	void Print()
	{
		cout << "A::Print()" << endl;
		cout << _a << endl;//这里的_a是通过this指针解引用访问的,空指针解引用!!!! 
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Print();
    //p->_a  = 1;//error,这里就是在类外面访问的,本来是被限制了的
	return 0;
}

运行结果:
image.png
程序运行崩,过程分析:
访问对应的成员变量,会传递对应对象的地址。而这里的地址为nullptr,通过nullptr->_a引起程序崩溃。

六、 C++和C语言实现Stack对比

6.1 封装

面向对象有三大特性:封装、继承、多态。
封装的概念: 用类将对象的属性(数据)与操作数据的方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
封装本质上是一种管理,让用户更方便使用类。 比如:对于电脑这样一个复杂的设备,提供给用户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。
类也是一样,我们使用类将数据和方法都封装起来。不想对外开放的就用 protected/private 封装起来,用 public 封装的成员允许外界对其进行合理的访问。 所以封装本质上是一种管理。

6.2 C语言实现Stack

//Stack.c
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}ST;
void STInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}
void STDestroy(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}
void STPush(ST* ps, STDataType x)
{
	assert(ps);
		// 满了, 扩容
	if (ps->top == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity *
			sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}
bool STEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}
void STPop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));
	ps->top--;
}
STDataType STTop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));
	return ps->a[ps->top - 1];
}
int STSize(ST* ps)
{
	assert(ps);

	return ps->top;
}
int main()
{
	ST s;
	STInit(&s);
	STPush(&s, 1);
	STPush(&s, 2);
	STPush(&s, 3);
	STPush(&s, 4);
	while (!STEmpty(&s))
	{
		printf("%d\n", STTop(&s));
		STPop(&s);
	}
	STDestroy(&s);//s.a[s.top]可以直接访问栈顶元素,但是不规范
                  //但是这种访问方式并不好,万一栈是空栈时,越界!
                  //所以用C语言这样的实现不怎么规范,而且存在风险!!!
	return 0;
}

6.3 C++实现Stack

//Stcak.cpp
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
	// 成员函数
	void Init(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}
	void Push(STDataType x)
	{
		if (_top == _capacity)
		{
			int newcapacity = _capacity * 2;
			STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
				sizeof(STDataType));
			if (tmp == NULL)
			{
				perror("realloc fail");
				return;
			}
			_a = tmp;
			_capacity = newcapacity;
		}
		_a[_top++] = x;
	}
	void Pop()
	{
		assert(_top > 0);
		--_top;
	}
	bool Empty()
	{
		return _top == 0;
	}
	int Top()
	{
		assert(_top > 0);
		return _a[_top - 1];
	}
	void Destroy()
	{
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	// 成员变量
	STDataType * _a;
	size_t _capacity;
	size_t _top;
};
int main()
{
	Stack s;
	s.Init();
	s.Push(1);
	s.Push(2);
	s.Push(3);
	s.Push(4);
	while (!s.Empty())
	{
		printf("%d\n", s.Top());
		s.Pop();
	}
	s.Destroy();
	return 0;
}

C++中数据和函数都放到了类里面,通过访问限定符进性了限制,不能再随意通过对象直接修改数据,这是C++封装的⼀种体现,这个是最重要的变化。这里的封装的本质是一种更严格规范的管理,避免出现乱访问修改的问题。
想想我们是如何管理陕西省的兵马俑的。我们若什么都不管,兵马俑就被随意破坏了。所以我们建立了一座房子将兵马俑封装起来。但是我们封装的目的不是为了不给别人看,所以我们开放了售票通道,人们可以通过买票突破封装,在合理的监管机制下进去参观。

在这里插入图片描述
创作不易,喜欢的uu记得三连支持一下哦!
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

如何用码上飞解决企微上真实需求来接单赚米

在企微的工作台中有一个「需求模块」&#xff0c;所有的企微用户都可以在上面提出自己的需求。 例如张三说“在企微上我怎么样才可以把一个客户发的语音&#xff0c;转给另一个客户听&#xff1f;” 李四说“我需要一个能每天在工作群里定时发布信息并能自动修改日期的功能。…

240713-Xinference模型下载、管理及测试

Step 1. 安装Xinference 安装 — Xinference Step 2. 下载模型 方式1: UI界面下载 命令行启动Xinference&#xff1a; xinference-local --host 0.0.0.0 --port 9997在localhost:9997中&#xff0c;通过左侧的Launch Model 配置并下载模型如果模型已下载或配置好&#xff…

好莱坞级别AI视频工具Odyssey亮相!AI世界动态回顾

好莱坞级别的视觉AI&#xff1a;Odyssey 首先&#xff0c;我们要提到的就是Odyssey——一款新晋AI视频工具&#xff0c;它以其好莱坞级别的视觉AI能力引起了广泛关注。奥德赛展示的一些片段令人印象深刻&#xff0c;包括精美的无人机镜头、风景画面以及专业级的B-roll素材。虽…

2024年公司电脑屏幕监控软件推荐|6款好用的屏幕监控软件盘点!

在当今的商业环境中&#xff0c;确保员工的工作效率和数据安全是每个企业管理者的重要任务。屏幕监控软件通过实时监控和记录员工的电脑活动&#xff0c;帮助企业有效地管理和优化工作流程。 1.固信软件 固信软件https://www.gooxion.com/ 主要特点&#xff1a; 实时屏幕监控…

python web自动化测试

安装 seleinum # 设置镜像 pip install statsmodels -i https://pypi.tuna.tsinghua.edu.cn/simple/ pip install selenium安装chrome driver 1、查看chrome的版本 2、https://googlechromelabs.github.io/chrome-for-testing/#stable 上面下载对应版本的chrome driver 只要…

Python | Leetcode Python题解之第230题二叉搜索树中第K小的元素

题目&#xff1a; 题解&#xff1a; class AVL:"""平衡二叉搜索树&#xff08;AVL树&#xff09;&#xff1a;允许重复值"""class Node:"""平衡二叉搜索树结点"""__slots__ ("val", "parent&quo…

快速理解java的volatile关键字

volatile有啥用呢&#xff1f; volatile是 JVM 提供的 轻量级 的同步机制&#xff08;有三大特性&#xff09; 保证可见性 不保证原子性 禁止指令重排 好汉你别激动&#xff0c;同步机制说白了就是 syhconized,这种机制你可以这样理解就是在一段时间内有序的发生操作。假如一…

创建SpringBoot聚合项目

创建SpringBoot聚合项目 需求&#xff1a;以仓库管理系统(warehouse management system)wms为例创建聚合项目 1、创建空项目文件夹 2、创建父工程 删掉src&#xff0c;配置夫工程的pom配置 <properties><maven.compiler.source>8</maven.compiler.source><…

【C++】C++入门实战教程(打造属于自己的C++知识库)

目录 目录 写在前面 1.C学习路线 2.本教程框架介绍 一.C基础部分 1.程序编码规范 2.程序运行与编译 3.关键字 4.常用数据类型 5.运算符相关 二.C进阶部分 1.面向对象编程 2.函数编程 3.模板编程 4.多线程与并发 5.STL介绍及使用 6.内存模型与优化 三.C实战部…

【vue教程】一. 环境搭建与代码规范配置

目录 引言Vue 框架概述起源与设计理念核心特性优势 Vue 开发环境搭建环境要求安装 Vue CLI创建 Vue 项目项目结构介绍运行与构建 组件实例基础模板响应式更新 代码规范为什么要使用代码规范在 Vue 项目中使用 ESLint 、PrettierESLint配置 ESLintrules 自定义错误级别 Prettier…

【Linux杂货铺】3.程序地址空间

1.程序地址空间的引入 fork(&#xff09;函数在调用的时候子如果是子进程则返回0&#xff0c;如果是父进程则返回子进程的pid&#xff0c;在代码中我们分别在子进程和父进程读取全局变量g_val的时候居然出现了俩个不同的值。如下&#xff1a; #include<stdio.h> #includ…

【Linux信号】阻塞信号、信号在内核中的表示、信号集操作函数、sigprocmask、sigpending

我们先来了解一下关于信号的一些常见概念&#xff1a; 实际执行 信号的处理动作 称为信号递达。 信号从产生到递达的之间的状态称为信号未决。 进程可以选择阻塞(Block)某个信号。 被阻塞的信号产生时是处于未决状态的&#xff0c;知道进程解除对该信号的阻塞&#xff0c;该…

01. 课程简介

1. 课程简介 本课程的核心内容可以分为三个部分&#xff0c;分别是需要理解记忆的计算机底层基础&#xff0c;后端通用组件以及需要不断编码练习的数据结构和算法。 计算机底层基础可以包含计算机网络、操作系统、编译原理、计算机组成原理&#xff0c;后两者在面试中出现的频…

FGF14:脑部疾病新潜力靶标

成纤维细胞生长因子14&#xff08;FGF14&#xff09;是FGF11亚家族成员&#xff0c;在神经元的所有基本特性&#xff08;内在放电、兴奋性和抑制性神经元的突触传递和可塑性&#xff09;中发挥作用。 &#xff08;数据来源AlphaFold&#xff09; FGF14由247个氨基酸组成&#x…

一键运行 ComfyUI SD3!大规模医学 VQA 评测数据集上线,涉及超 20 个人体器官和部位

为了进一步推进 AI4S 的普适化&#xff0c;HyperAI超神经策划了「Meet AI4S」系列直播栏目。第一期直播将于 7 月 17 日 19:00 准时上线&#xff01; 我们邀请到了浙江大学遥感与地理信息系统博士生丁佳乐&#xff0c;他的分享主题为「神经网络为房价的空间异质性提供新解释」&…

c# 容器变换

List<Tuple<int, double, bool>> 变为List<Tuple<int, bool>>集合 如果您有一个List<Tuple<int, double, bool>>并且您想要将其转换为一个List<Tuple<int, bool>>集合&#xff0c;忽略double值&#xff0c;您可以使用LINQ的S…

Python函数 之 变量

1.引用【了解】 定义变量的时候, 变量和数据 都会在内存开辟空间 变量所对应的内存空间中存储的是 数据所在内存的地址 (平时理解为 将数据存储到变量中即可) 变量中保存数据的地址,就称为是引用 Python 中所有数据的传递,传递的都是引用(即地址) 赋值运算符(), 会改变变量的引…

全面升级的对象创建——抽象工厂模式(Python实现和JAVA实现)

1. 引言 大家好&#xff01;在之前的文章中&#xff0c;我们探讨了简单工厂和工厂方法模式&#xff1a; 轻松创建对象——简单工厂模式&#xff08;Python实现&#xff09; 轻松创建对象——简单工厂模式&#xff08;Java实现&#xff09; 灵活多变的对象创建——工厂方法模式…

鸿蒙瀑布流和欢迎页(1)

1.瀑布流 https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-arkui/arkui-ts/ts-container-waterflow.md他有官网文档&#xff0c;有个瀑布流的案例 自定义一类实现官方瀑布流接口 // WaterFlowDataSource.ets// 实现IDataSource接口的…

开发技术-Java BigDecimal 精度丢失问题

文章目录 1. 背景2. 方法3. 总结 1. 背景 昨天和小伙伴排查一个问题时&#xff0c;发现一个 BigDecimal 精度丢失的问题&#xff0c;即 double a 1.1;BigDecimal ba new BigDecimal(a).subtract(new BigDecimal(0.1));System.out.println(ba);输出&#xff1a; 1.000000000…