C++——类和对象1

news2025/1/16 1:05:03

目录

1. 类和对象认识

2. 类的引入 

3. 类的定义 

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

4.1 访问限定符 

4.2 封装 

5. 类的作用域 

6. 类的实例化

7. 类对象模型

7.1 如何计算类对象的大小 

7.2 类对象的存储方式猜测  

7.3 结构体内存对齐规则

8. this指针

8.1 this指针的引出 

8.2 this指针的特性


1. 类和对象认识

C语言是 面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题
C++是基于 面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成

对于同样一件事,比如说去超市买东西,对于C语言来说可能需要做如下工作

  1. 实现一个出门并且找到超市的函数
  2. 实现一个在超市里挑东西的函数
  3. 实现一个结账的函数
  4. ...

对于C++而言,并不关注是怎么完成买东西这个过程的,而是关注在这个过程中有几类"人"

  • 对于买东西的人,他需要知道如何找到超市,需要知道要买什么东西
  • 对于超市,它需要有商品,需要提供服务员引导,需要提供结账的服务

因此C++并不关注流程,而是关注每一个对象在这个流程中完成什么工作

 

2. 类的引入 

C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数

在C语言中,栈的结构体定义内只能有变量,函数不能在结构体内部

// C语言实现的栈
typedef int DataType;
 
struct Stack {
	DataType* a;
	int top;
	int capacity;
};

void StackInit(struct Stack* ps);
void StackPush(struct Stack* ps, DataType x);
void StackPop(struct Stack* ps);

void StackInit(struct Stack* ps){}

void StackPush(struct Stack* ps, DataType x){}

void StackPop(struct Stack* ps){}

int main()
{
	struct Stack s;

	StackInit(&s);
	StackPush(&s, 10);
	StackPop(&s);

	return 0;
}

由于C++兼容C,因此C++内可以用继续用struct来定义结构体,但C++是面向对象的,C++的结构体内也可以定义函数,这样的结构体是在C的基础上升级成了

// 成员变量和成员函数可以在同一个结构体内
struct Stack
{
	void Init(size_t cap)
	{

		a = (DataType*)malloc(sizeof(DataType) * cap);
		if (nullptr == a)
		{
			perror("malloc申请空间失败");
			return;
		}

		capacity = cap;
		top = 0;
	}

	void Push(DataType x)
	{
		if (top <= capacity - 1)
		{
			a[top++] = x;
		}

		else
		{
			// ...
		}
	}


	void Pop()
	{}

	DataType* a;
	size_t top;
	size_t capacity;
};

int main()
{
	Stack s;

	s.Init(4);
	s.Push(1);
	s.Push(2);
	s.Pop();

	return 0;
}

上面结构体的定义,在C++中更喜欢用class来代替

 

3. 类的定义 

类的定义格式如下

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

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

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

类体中内容称为类的成员:

  • 类中的变量称为类的属性或成员变量
  • 类中的函数称为类的方法或者成员函数

类的两种定义方式:

  1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理
  2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
// 定义方式一
class Student 
{
public:
	void Showinfo()   // 成员函数,显示学生信息
	{
		// ...
	}

public:
	char* _name;      // 成员变量,姓名
	char* _sex;       // 成员变量,性别
	size_t age;       // 成员变量,年龄

};

一般情况下,更建议采用函数声明与定义分离的写法,便于查看类内部功能 

// 定义方式二

<student.h>
class Student
{
public:
	void Showinfo();   // 成员函数,显示学生信息,这里只是声明

public:
	char* _name;      // 成员变量,姓名
	char* _sex;       // 成员变量,性别
	size_t age;       // 成员变量,年龄

};

_________________________________________________________________________________________

<student.cpp>
	void Stduent::showinfo()   // 函数的定义,显示学生信息,要加类名
	{
		// ...
	}

命名规则建议

  • 一般建议在成员变量的前面加上前缀_
  • 函数名、类名等所有单词的首字母大写
  • 变量首字母小写,后面单词首字母大写

 

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

C++实现封装的方式:用类将对象的属性与方法结合在一块,(就是把成员变量和成员函数都放在类的内部),让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用 

4.1 访问限定符 

C++的访问限定符有三种:

  • public(公有)
  • private(私有)
  • protected (保护)

【访问限定符说明】 

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

代码一:函数showinfo和变量a均被public修饰,可以在类外被访问到

class Student
{
public:
	void showinfo()   // 成员函数,显示学生信息
	{
		cout << "I'm public, you can see me" << endl;
	}

	int a = 10;               

};

int main()
{
	Student zs;
	zs.showinfo();
	cout << zs.a << endl;

	return 0;
}

代码二:函数showinfo和变量a分别被private和protected修饰,不能在类外访问

class Student
{
private:
	void showinfo()   // 成员函数,显示学生信息
	{
		cout << "I'm public, you can see me" << endl;
	}

protected:
	int a = 10;
};

int main()
{
	Student zs;
	zs.showinfo();         // “Student::showinfo”: 无法访问 private 成员(在“Student”类中声明)
	cout << zs.a << endl;  // “Student::a”: 无法访问 protected 成员(在“Student”类中声明)

	return 0;
}

代码三:private的作用域遇到public就结束了

class Student
{
private:
	void showinfo()   // 成员函数,显示学生信息
	{
		cout << "I'm public, you can see me" << endl;
	}

public:
	int a = 10;
};

int main()
{
	Student zs;
	zs.showinfo();         // “Student::showinfo”: 无法访问 private 成员(在“Student”类中声明)
	cout << zs.a << endl;  // 可以访问a

	return 0;
}

代码四:class的默认访问权限为private,struct为public,因此

class A
{
	void showinfo() 
	{}
};

struct B
{
	void showinfo()
	{}
};

int main()
{
	A a;
	a.showinfo();    // “A::showinfo”: 无法访问 private 成员(在“A”类中声明)

	B b;
	b.showinfo();    // 正常访问


	return 0;
}

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

 

4.2 封装 

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

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

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

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

 

5. 类的作用域 

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

class A
{
public:
	void show();            // 函数声明
};

void A::show()              // 函数定义,要加上类名
{
	cout << "you got me" << endl;
}

int main()
{
	A a;
	a.show();
	return 0;
}

 

6. 类的实例化

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

类就像一张图纸,本身并不为内部的变量开辟空间,只有当我们拿这个图纸(类)将房子(对象)创建出来,才会在计算机上真正生成一个我们要的有血有肉的对象

对于类内部的成员变量,都只是声明。对于变量而言,开空间就是定义,不开空间就是声明

int age;                      // 变量的定义
extern int age;               // 变量的声明

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

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

 

7. 类对象模型

7.1 如何计算类对象的大小 

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

int main()
{
	cout << sizeof(A) << endl;    // 1
	return 0;
}

这里得到的类的大小为1 ,和对象的存储方式与结构体对齐紧密相关

 

7.2 类对象的存储方式猜测  

🔶 存储方式一:对象中包含类的各个成员

对于一个类实例化出来的每个对象,都会持有自己的成员函数和成员变量,而每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间

🔶 存储方式二:对象中包含成员变量和成员函数的地址,代码只保存一份,在对象中保存存放代码的地址

🔶 存储方式三:只保存成员变量,成员函数存放在公共的代码段(与第二种的区别是对象内不保存函数的指针)

用下面的代码来测试究竟使用的是哪种存储方式

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

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

// 类中什么都没有---空类
// 没有成员变量的类对象,给1字节占位,不存储实际数据,标识对象存在
class A3
{};

int main()
{
	cout << sizeof(A1) << endl;   // 4
	cout << sizeof(A2) << endl;   // 1
	cout << sizeof(A3) << endl;   // 1
	return 0;
}

可以看到,A1类的大小为4,说明成员函数不在A1对象的空间内,如果是方案一,A1的大小应该是一个int加一个指针,大小为12;同理方案二也是一样,因此实际上对象的存储方式是方案三——一个对象内只包含成员变量,而成员函数则在公共代码区,并不在对象内也没有指针

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

和普通函数一样,当需要访问成员函数时,在编译链接时,通过符号解析和重定位找到函数的地址,再call成员函数

用下面的一个类来研究成员函数在内存中的位置 

class A
{
public:
	void func()
	{}

private:
	int _a;
};

int main()
{
	A a1;
	A a2;

	a1.func();
	a2.func();
	return 0;
}

得到的main函数汇编如下: 


000000000040064d <main>:
  40064d:	55                   	push   %rbp
  40064e:	48 89 e5             	mov    %rsp,%rbp
  400651:	48 83 ec 20          	sub    $0x20,%rsp
  400655:	48 8d 45 f0          	lea    -0x10(%rbp),%rax
  400659:	48 89 c7             	mov    %rax,%rdi
  40065c:	e8 65 00 00 00       	callq  4006c6 <_ZN1A4funcEv>
  400661:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
  400665:	48 89 c7             	mov    %rax,%rdi
  400668:	e8 59 00 00 00       	callq  4006c6 <_ZN1A4funcEv>
  40066d:	b8 00 00 00 00       	mov    $0x0,%eax
  400672:	c9                   	leaveq 
  400673:	c3                   	retq  

 符号表为:

mbol table '.symtab' contains 20 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS obj.cpp
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     5: 0000000000000000     1 OBJECT  LOCAL  DEFAULT    5 _ZStL8__ioinit
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     7: 0000000000000027    61 FUNC    LOCAL  DEFAULT    2 _Z41__static_initializati
     8: 0000000000000064    21 FUNC    LOCAL  DEFAULT    2 _GLOBAL__sub_I_main
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT   10 
    11: 0000000000000000     0 SECTION LOCAL  DEFAULT   11 
    12: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 
    13: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
    14: 0000000000000000    10 FUNC    WEAK   DEFAULT    6 _ZN1A4funcEv
    15: 0000000000000000    39 FUNC    GLOBAL DEFAULT    2 main
    16: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSt8ios_base4InitC1Ev
    17: 0000000000000000     0 NOTYPE  GLOBAL HIDDEN   UND __dso_handle
    18: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSt8ios_base4InitD1Ev
    19: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __cxa_atexit

可以看到,第14行func的Ndx=6,说明对于func的引用是一个.rel.text节的条目,需要引用一个.text节的函数,汇编中也能看到两次call的是同一个地址4006c6,而这个程序的.text节从400560-400744,所以可以确定func是位于公共代码段的一个函数

12 .text         000001e2  0000000000400560  0000000000400560  00000560  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .fini         00000009  0000000000400744  0000000000400744  00000744  2**2

因此,下面的程序实际上是能够正常运行的

class A
{
public:
	void func(){}
};

int main()
{
	A* ptr = nullptr;
	ptr->func();       // 这里虽然是一个空指针,但并不会去对象内找func,而是在公共代码段找func,因此能找到,程序正常运行
	return 0;
}

7.3 结构体内存对齐规则

类的成员函数不进入对象内存的计算,因此成员变量和结构体的对齐规则相同

数组、结构体、联合、位断、枚举_七月不远.的博客-CSDN博客

结构体内存对齐规则

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

 

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;
	d1.Init(2008, 1, 1);

	Date d2;
	d2.Init(2008, 1, 2);

	d1.Print();
	d2.Print();
	return 0;
}

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

我们已经知道,类中的成员函数在公共代码段,那么两个对象去调用同一个函数时,所用到的是同一份代码,因此必须要有一种机制来让成员函数能够区分出不同的对象,比如:

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

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

编译器会分别对Init和Print进行处理

	void Init(Date* const this, int year, int month, int day)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}

	void Print(Date* const this)
	{
		cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
	}

同时在main函数内部也会对调用进行处理 

int main()
{
	Date d1;
	d1.Init(&d1, 2008, 1, 1);

	Date d2;
	d2.Init(&d2, 2008, 1, 2);

	d1.Print(&d1);
	d2.Print(&d2);

	return 0;
}

其实对象在每次调用成员函数时,都会隐式地把自己的地址作为形参传给该成员函数,成员函数用一个this指针来接收,此后在成员函数内部所有需要用到成员变量的地方,都会隐式地加上this指针,它指明了这个成员变量是某个已经实例化的对象中的变量,这样就能把不同的对象区分开来

C++语法规定,在实参和形参的位置不能显式地传递和接收this指针,但可以在函数内部可以使用this指针(即使不使用也会右编译器自动增加),但this指针不能被修改,因为this指针被const所修饰,但this指针指向的内容(成员变量)可以被修改

考虑下面的代码,这个程序会崩溃,原因是在PrintA函数中_a是通过this->_a找到的,而this为nullptr

class A
{
public:
	void PrintA()
	{
		cout << _a << endl;   // 空指针解引用
	}

private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->PrintA();

	return 0;
}

this指针存放在上,因为this是一个形参,也可能被编译器所优化,存放在寄存器

对于下面的程序进行分析

class A
{
public:
	void PrintA()
	{
		cout << this << endl;
	}

private:
	int _a;
};

int main()
{
	A aa;
	aa.PrintA();

	return 0;
}

objdump得到main函数的汇编代码

00000000004007ad <main>:
  4007ad:	55                   	push   %rbp
  4007ae:	48 89 e5             	mov    %rsp,%rbp
  4007b1:	48 83 ec 10          	sub    $0x10,%rsp
  4007b5:	48 8d 45 f0          	lea    -0x10(%rbp),%rax  // 将帧寄存器-16处的地址传给寄存器%rax
  4007b9:	48 89 c7             	mov    %rax,%rdi         // 将%rax里的地址传给%rdi作为函数调用的第一个参数
  4007bc:	e8 59 00 00 00       	callq  40081a <_ZN1A6PrintAEv> // call PrintA函数
  4007c1:	b8 00 00 00 00       	mov    $0x0,%eax
  4007c6:	c9                   	leaveq 
  4007c7:	c3                   	retq   

前三行是为main函数开辟栈帧,第四行lea指令加载对象aa的有效地址到%rax中,第五行%rax里的地址传给%rdi作为函数调用的第一个参数,第六行调用PrintA

接下来我们看看PrintA里面是如何保存this(%rdi里的值)的

000000000040081a <_ZN1A6PrintAEv>:
  40081a:	55                   	push   %rbp
  40081b:	48 89 e5             	mov    %rsp,%rbp
  40081e:	48 83 ec 10          	sub    $0x10,%rsp
  400822:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
  400826:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  40082a:	48 89 c6             	mov    %rax,%rsi
  40082d:	bf 60 10 60 00       	mov    $0x601060,%edi
  400832:	e8 59 fe ff ff       	callq  400690 <_ZNSolsEPKv@plt>
  400837:	be b0 06 40 00       	mov    $0x4006b0,%esi
  40083c:	48 89 c7             	mov    %rax,%rdi
  40083f:	e8 5c fe ff ff       	callq  4006a0 <_ZNSolsEPFRSoS_E@plt>
  400844:	c9                   	leaveq 
  400845:	c3                   	retq   
  400846:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
  40084d:	00 00 00 

观察到第四行,在PrintA内部,将%rdi里的值保存到了帧寄存器-8处,说明在linux下的this指针存放在栈上

 

8.2 this指针的特性

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

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

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

相关文章

cv-cuda (cvcuda、nvcv)教程——Python安装

由于当前版本安装后&#xff0c;大家反应import nvcv cvcuda 失败&#xff0c;看官方文档&#xff0c;当前还不是很规范&#xff0c;特此记录当前版本的安装方法。 官方安装文档&#xff1a;Installation — CV-CUDA Alpha documentation 方法一、如果你有权限推荐deb安装方式…

机器学习第15章-规则学习

机器学习第15章-规则学习 以下列出我觉得重要&#xff0c;在编码的思路中可以参考的地方 冲突消融 当一条规则的判断出现不同的结果时&#xff0c;解决冲突的方法 1.投票法 2.排序法 3.无规则法 序贯覆盖 生成规则过程中去除当前规则所能覆盖的数据 生成方式 自顶向下…

双软认证的好处,赶紧来看看吧

1、“双软件”认可对企业有什么好处&#xff1f; 对于认定的软件企业&#xff0c;从盈利年度起&#xff0c;第一年和第二年免征企业所得税&#xff0c;第三年至第五年减半征收企业所得税&#xff0c;即两免三减。对认定软件产品的企业&#xff0c;对实际增值税负担超过3%的部分…

【ONE·C++ || vector (二)】

总言 主要讲述vector的模拟实现。 文章目录总言1、基本框架搭建&#xff1a;成员变量2、对构造函数、析构函数3、增删查改、空间扩容3.1、vector::push_back、vector::pop_back3.2、vector::reserve、vector::capacity、vector::size3.3、operator[ ]3.4、遍历&#xff1a;迭代…

记录robosense RS-LIDAR-16使用过程1

拿到设备&#xff0c;首先对照型号去官网下载相关资料&#xff08;用户手册/软件/SDK&#xff09;,需要填写资料https://www.robosense.ai/resources-27工业相机通常也有出厂SDK文件&#xff0c;之前有使用知微传感的D130相机&#xff0c;也是先安装SDK、看手册然后使用。大型厂…

【Java集合】Map接口常用方法及实现子类

文章目录01 Map 接口实现类的特点02 Map 接口和常用方法03 Map 接口遍历方法04 HashMap 用例 小结05 HashMap 底层&扩容机制06 Hashtable07 PropertiesMap为双列集合&#xff0c;Set集合的底层也是Map&#xff0c;只不过有一列是常量所占&#xff0c;只使用到了一列。 01 …

国科大《高级人工智能》沈老师部分——行为主义笔记

国科大《高级人工智能》沈老师部分——行为主义笔记 沈华伟老师yyds&#xff0c;每次上他的课都有一种深入浅出的感觉&#xff0c;他能够把很难的东西讲的很简单&#xff0c;听完就是醍醐灌顶&#xff0c;理解起来特别清晰今年考试题目这部分跟往年基本一样&#xff0c;沈老师画…

长城汽车2022年销量106万辆,20万以上车型占比15%

2023年&#xff0c;长城汽车预计将推出超10款新能源车型&#xff0c;发力新能源和智能化。1. 年度销量&#xff1a;超106万辆 根据长城最新发布的产销数据&#xff1a;•2022年&#xff0c;长城汽车全年销售1,067,523辆&#xff1b; •其中&#xff0c;海外市场累计销售173,180…

2022CTF培训(十二)IOT 相关 CVE 漏洞分析

附件下载链接 NETGEAR R7800&#xff08;CVE-2020-11790&#xff09; NETGEAR R7800 存在命令注入漏洞&#xff0c;下面以 V1.0.2.62 版本固件为例进行介绍。 固件仿真 漏洞存在于 uhttpd 中&#xff0c;由于该功能比较独立&#xff0c;可以直接用 qemu user mode 仿真。 /…

在 anaconda 中安装 tensorflow models (gpu)

环境&#xff1a;Windows; Intel CPU Nvidia GPU 1. 创建环境 不推荐单次安装过多的库&#xff0c;可能导致安装失败&#xff08;如超出终端缓存等&#xff09;注意添加库的顺序 tensorflow-gpu 需要在 cudatoolkit 之前否则下载的 tensorflow-gpu 不支持 gpu 「实测」 TODO…

设备注册挂载流程(包含上电、使能、i2c通讯介绍)

目录 简介 上电时序 电压不同 时序不同 使能与复位 CLK时钟 I2C通讯 主从关系 识别设备 通讯格式 简介 任何相对于主板芯片的外挂设备都需要一定的注册挂载流程 &#xff08;外挂设备&#xff1a;比如摄像头、nfc芯片、显示屏等等&#xff09; 设备的挂载则需要满足…

JAVAEE-多线程(4)

目录 定时器 实现自己的Timer 线程池 常见的锁策略&#xff1a; 乐观锁和悲观锁 读写锁和普通互斥锁 重量级锁和轻量锁 自旋锁和挂起等待锁 公平锁和非公平锁 可重入锁和不可重入锁 synchronized CAS CAS和ABA问题 锁粗化 JUC 原子类 Semaphore CountDownLatc…

CAN总线控制器MCP2515 替代芯片 DP2515 DP2515-I/ST

汽车K总线与CAN的区别是什么 1、功能不同   K线一般用于检测系统&#xff0c;属单线模式&#xff0c;与诊断仪器连接并相互传递数据。CAN线主要用于控制单元与控制单元之间传递数据、属双线模式&#xff0c;分高位线和地位线。   2、通讯速度不同   K线通讯速率较低&…

101.对称二叉树 | 递归 + 迭代

对称二叉树 leetcode : https://leetcode.cn/problems/symmetric-tree/ 参考 对称二叉树 递归思路 首先在开始时, 一定要注意, 对称二叉树对比的并不是一个节点的左右子树, 而是两棵树, 这个很关键! 对比时是内侧和内侧对比, 外侧和外侧对比, 递归三步 : 确定递归的参数以…

1.1.2 了解JAVA语言

文章目录1 JAVA语言发展史2 面向对象的概念3 跨平台性4 JDK1 JAVA语言发展史 JAVA是由詹姆斯•高斯林&#xff08;James Gosling&#xff09;所创建的&#xff0c;其1977年获得了加拿大卡尔加里大学计算机科学学士学位&#xff0c;1983年 获得了美国卡内基梅隆大学计算机科学博…

4)Mybatis数据源以及事务实现

1. Mybatis数据源分为两种&#xff0c;一种直接连接数据库&#xff0c;一种使用连接池连接数据库&#xff0c;具体代码实现在包目录下 org.apache.ibatis.datasource 数据源接口&#xff1a; javax.sql.DataSource 池化数据源&#xff1a; org.apache.ibatis.datasource.…

OpenGL集锦(1)-安装与概述

目录概述&#xff46;&#xff45;&#xff44;&#xff4f;&#xff52;&#xff41;下安装编写OpenGL应用程序测试hello,world概述 OpenGL&#xff08;英语&#xff1a;Open Graphics Library&#xff0c;译名&#xff1a;开放图形库或者“开放式图形库”&#xff09;是用于…

Lichee_RV学习系列--CoreMark-Pro移植

Lichee_RV学习系列文章目录 Lichee_RV学习系列—认识Lichee Rv Dock、环境搭建和编译第一个程序 Lichee_RV学习系列—移植dhrystone 文章目录Lichee_RV学习系列文章目录一、CoreMark-Pro简介二、获取源码三、编译coremark-pro1、配置coremark-pro2、编译coremark-pro四、开发板…

各种树的总结

1.B树和B树 数据库的大量数据用什么存储&#xff1f;为什么是B树和B树&#xff1f;使用二叉树不行吗&#xff1f;先来说说他们的演变吧&#xff0c;首先如果用二叉树的话都为排好序的树查询起来是不是效率不高&#xff1f;所以此时我们提出了对树排序&#xff0c;就变成了二叉…

联想拯救者屏幕亮度无法调节,监视器和显卡驱动问题,经过多种测试

主要的问题位置 1&#xff0c;设备管理器中的监视器部分 2&#xff0c;设备管理器的显卡适配器部分 个人电脑出现这种情况的原因 自己拆一下机器加装固态&#xff0c;但这种感觉不应该导致问题。但导致这种问题的原因可能是装固态时候把电池拔了。 一些网上常说的方法 更新…