C++ Primer 第十二章 动态内存 重点解读

news2024/10/6 5:58:08

文章目录

  • 1 动态内存与智能指针
    • 1.1 `shared_ptr`类
      • 1.1.1 `shared_ptr`的方法
      • 1.1.2 make_shared函数
    • 1.2 智能指针和异常
    • 1.3 `unique_ptr`
      • 1.3.1 传递unique_ptr参数和返回unique_ptr
      • 1.3.2 向unique_ptr传递删除器
      • 1.3.3 习题补充
    • 1.4 `weak_ptr`
  • 2 直接内存管理
    • 2.1 `new`
    • 2.2 `delete`
    • 2.4 shared_ptr 和 new 结合使用
      • 2.4.1 不能将一个内置指针隐式类型转换成一个智能指针
      • 2.4.2 不要混合使用普通指针和智能指针
      • 2.4.3 不要使用get()初始化另一个智能指针或为智能指针赋值
  • ※3 控制内存分配——第十九章 P726
    • 3.1 重载new和delete
      • 3.1.1 **`new`表达式调用的详细步骤**——三步
      • 3.1.2 **`delete`表达式调用的详细步骤**——两步
      • 3.1.3[`operator new`接口和`operator delete`接口](https://en.cppreference.com/w/cpp/memory/new/operator_new)
    • 3.2 `operator new` 内使用`malloc`
    • 3.3 `placement new`——定位new表达式
  • 4 动态数组
    • 4.1 new 和数组
    • 4.2智能指针和动态数组
      • 4.2.1 通过`unique_ptr`管理 —— `unique_ptr`通过默认模板实参调用`delete[]`
      • 4.2.2 通过`shared_ptr`管理
      • 4.2.2 详细的智能指针删除器区别看第十六章—1.9
    • 4.2 `allocator`类
      • 4.2.1 allocator分配未构造的内存
      • 4.2.2 `construct_at`与`destroy_at`——since C++20
      • 4.2.3 拷贝和填充未初始化内存的算法
      • 4.2.3 拷贝和填充未初始化内存的算法

1 动态内存与智能指针

为了更安全的使用动态内存,标准库两种智能指针

  • shared_ptr允许多个指针指向同一个对象
  • unique_ptr“独占”所指向的对象

标准库定义了一个伴随类

  • weak_ptr,它是一种弱引用,指向shared_ptr所管理的对象

​ 三者都定义在memory头文件中

1.1 shared_ptr

1.1.1 shared_ptr的方法

  • shared_ptrunique_ptr都支持的操作
shared_ptr<T> p空智能指针,可以指向类型为T的对象
p将p用作一个条件判断,若p指向一个对象,则为true
*p解引用p,获得它指向的对象
p->等价于(*p).member
p.get()返回p中保存的指针。要小心使用,若智能指针释放了其对象,返回的指针所指向的对象也消失了,从而造成悬空指针
swap(p, q)p.swap(q)交换p和q中的指针
  • shared_ptr独有的操作
make_shared<T>(args)返回一个shared_ptr,指向一个动态分配的类型T的对象,使用args初始化对象
shared_ptr<T>p(q)p是shared_ptr的拷贝;此操作会递增q中的引用计数。q中的指针必须能转换为T*
p = qp和q都是shared_ptr,所保存的指针必须能互相转换。此操作会递减p的引用计数,递增q的引用计数;若p的引用计数变为0,则将其管理的原内存释放
p.use_count()返回与p共享对象的智能指针数量(返回引用计数)
p.unique()p.use_count()为1,返回true;否则返回false;表示p是否为唯一指向这一对象的智能指针

1.1.2 make_shared函数

#include <memory>   //std::shared_ptr; std::make_shared
#include <iostream>  
#include <string>
#include <vector>
using namespace std;

int main() {
	shared_ptr<string> p = make_shared<string>(5, 'x');
	cout << *p << endl;    //output: xxxxx

	vector<string> v;
	v.emplace_back(5, 'x');
	cout << v[0] << endl;    //output: xxxxx
}

image-20231023005331794

类似顺序容器的emplace成员,make_shared用其参数来构造给定类型的对象。例如,调用make_shared<string>时传递的参数必须与string的某个构造函数相匹配

1.2 智能指针和异常

1.3 unique_ptr

shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定对象。当我们定义一个unique_ptr时,需要将其绑定一个new返回的指针上。由于一个unique_ptr拥有它指向的对象,因此unique_ptr不支持普通的拷贝和赋值操作支持移动赋值或移动构造

  • unique_ptr操作(其余相同操作参见shared_ptr表一)
unique_ptr<T> u1unique_ptr<T, D> u2unique_ptr,可以指向类型为T的对象。u1会使用delete来释放它的指针;u2会使用类型为D的的可调用对对象来释放它的指针
unique_ptr<T, D> u(d)unique_ptr,指向类型为T的对象,用类型为D的对象d代替delete
u = nullptr释放u指向的对象,将u置为空

image-20231023224017234

  • 允许赋值一个右值unique_ptr(见1.3.1)和nullptr
u.release()u放弃对指针的控制权,返回指针,并将u置为空
u.reset()u.reset(q)u.reset(nullptr)释放u指向的对象。如果提供了内置指针q,则令u指向这个对象;否则将u置为空

重点:

unique_ptr<int> p1 = new int(1024);
unique_ptr<int> p2(p1.release());    //release将p1置为空
unique_ptr<int> p3 = new int(4201);
p2.reset(p3.release());    //reset释放了p2原来指向的内存

p2.release();   //错误:p2不会释放内存,并且我们丢失了p2原来绑定的指针
p2.reset();     //正确,p2指向的对象直接被释放
auto p = p2.release();  //正确,要记得delete(p)
  • release成员返回unique_ptr当前保存的指针并将其置空。因此p2别初始化为p1原来保存的指针,而p1被置为空
  • reset成员接收一个可选的指针参数,令unique_ptr重新指向给定的指针。如果unique_ptr不为空,他原来指向的对象将被释放。

1.3.1 传递unique_ptr参数和返回unique_ptr

image-20231024223349117

image-20231024223536147

以上两图分别表示了C++11中为类提供的移动语义,也就是说虽然unique_ptr不允许普通的拷贝,但是我们可以通过移动构造或移动赋值,转移一个将要被销毁的unqiue_ptr的所有权。

unique_ptr<int> clone(int p) {
    return unique_ptr<int>(new int(p));    //纯右值?
}

unique_ptr<int> clone(int p) {
    unique_ptr<int> ret = new int(p);
    return ret;    //将亡值?
}

1.3.2 向unique_ptr传递删除器

  • 重载一个unique_ptr中的删除器会影响到unique_ptr类型以及如何构造(或reset)该类型函数对象
  • 第十六章:1.9

1.3.3 习题补充

  1. 下面的unique_ptr声明中,哪些时合法的,哪些可能导致后续的程序错误?—— P420
  1. shared_ptr为什么没有release成员?

releaseunique_ptr的成员函数,用于将指针的所有权转移给另一个unique_ptr。 因为shared_ptr是多对一的关系,一个shared_ptr交出控制权,其它shared_ptr依旧可以控制这个对象。因此这个方法对shared_ptr无意义。

1.4 weak_ptr

  • weak_ptr指向一个shard_ptr管理的对象,且不会改变引用计数
weak_ptr<T> w
weak_ptr<T> w(sp)shared_ptr sp指向相同的对象,T必须能转换为sp指向的类型
w = p
w.reset()将w置为空
w.use_count()与w共享对象的shared_ptr的数量
w.expired()return w.use_count == 0 ? true : false
w.lock()如果expiredtrue,返回一个空shared_ptr,否则返回一个指向w的对象的shared_ptr

由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock

std::shared_ptr<std::vector<std::string>> StrBlobPtr::check(std::size_t i, const std::string &msg) const {
    auto ret = wptr.lock();   //
    if (!ret) 
        throw std::runtime_error("unbound StrBlobPtr");
    if (i >= ret=>size())
        throw std::out_of_range(msg);
   return ret;
}

2 直接内存管理

《C++ Primer》P726-730 、P407-411

2.1 new

image-20231027000224816

image-20231027000120515

	/*列表初始化*/
	vector<int>* p = new vector<int>{ 0,1,2,3,4,5,6,7 };    

	/*值初始化*/
	string* ps1 = new string;    //默认初始化为空string
	string* ps2 = new string();  //值初始化为空string
	int* pi1 = new int;          //默认初始化; *pi1的值未定义
	int* pi2 = new int();        //值初始化为0;*pi2的为0

	/*动态分配const对象*/
	const string* ps = new const string;
  • 单一初始化器才能使用auto
	auto p1 = new auto("xxx");   //p1指向一个与"xxx"类型相同的对象,该对象用"xxx"初始化
	auto p2 = new auto{ "x", "xx" };   //error:括号中智能有单个初始化器

2.2 delete

image-20231027000025715

image-20231027000056125

2.4 shared_ptr 和 new 结合使用

2.4.1 不能将一个内置指针隐式类型转换成一个智能指针

如前所述,如果我们不初始化一个智能指针,他就会被初始化为一个空指针。我们还可以用new返回的指针来初始化智能指针

	shared_ptr<int> p(new int(1024));

	shared_ptr<int> p2 = new int(1024); //errror:不存在从int*到shared_ptr<int>的适当构造函数

接收指针传参的智能指针构造函数时explicit的。因此我们不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化方式来初始化一个智能指针

​ `` `

我们可以将智能指针绑定到一个指向其他类型的资源的指针上,但是为了这样做,必须提供自己的操作来替代delete

  • 定义和改变shared_ptr的其他方法
shared_ptr<T> p(q)p管理内置指针q所指向的对象;q必须是指向new分配的内存,且能够转换为T*类型
shared_ptr<T> p(u)p从unique_ptr u那里接管了对象的所有权;将u置为空
shared_ptr<T> p(q, d)p接管了内置指针q所指向的对象的所有权。q必须能转换为T*类型。p将使用可调用对象d(删除器)来替代delete
shared_ptr<T> p(p2, d)p是shared_ptr p2的拷贝,唯一的区别是p将用可调用对象d来替代delete
p.reset()p.reset(q)p.reset(q, d)若p是为一直想其对象的shared_ptrreset会释放此对象。若传递了可选的参数内置指针q,会令p指向q,否则会将p置为空。若还传递了参数d,将会调用d而不是delete来释放q

2.4.2 不要混合使用普通指针和智能指针

#include <memory>   //std::shared_ptr; std::make_shared
#include <iostream>  
using namespace std;

void process(shared_ptr<int> ptr) {}

int main() {
	int* x = new int(1024);
	process(x);   //error
	process(shared_ptr<int>(x));   //合法的,但内存会被释放
	int j = *x;  //未定义的:x是一个悬空指针!!!
	cout << j << endl;    //打印随机值,并不是1024
}

我们将一个临时的shared_ptr传递给process,当调用所在的表达式结束时,这个临时对象就被销毁了。销毁这个临时变量会递减引用计数,此时引用计数变为0了。因此,当临时对象被销毁时,它所指向的内存会被销毁

2.4.3 不要使用get()初始化另一个智能指针或为智能指针赋值

	shared_ptr<int> p(new int(1024));
	int* q = p.get();
	process(shared_ptr<int>(q));
	int foo = *p;
	cout << foo << endl;

结果与上述类似

※3 控制内存分配——第十九章 P726

3.1 重载new和delete

string *sp = new string("xxxx");
string *arr = new string[10];    //分配十个默认初始化的对象

delete sp;
delete[] arr;

3.1.1 new表达式调用的详细步骤——三步

  1. 调用一个名为operator new(或者operator new[])的标准库函数,该函数分配一块足够大的、原始的、未命名的内存空间以便存储特定类型的对象(或者对象数组)。

  2. 编译器运行相应的构造函数以构造这些对象,并为其传入初始值。

  3. 对象被分配了空间并构造完成,返回一个指向该对象的指针

3.1.2 delete表达式调用的详细步骤——两步

  1. 对sp所指的对象或者arr所指的数组中的元素执行对应的析构函数
  2. 编译器调用名为operator delete(或者operator delete[])的标准库函数释放内存空间
  • 应用程序可以在全局作用域中定义operator new函数和operator delete函数,也可以将他们定义为成员函数,如果被分配(释放)对象是类类型,则编译器首先在类以及基类的作用域中查找
  • 并且我们可以通过作用域限定符令newdelete表达式直接执行全局作用域中的版本
//删除类中的new和delete,并使用全局的new和delete
class Test {
public:
	Test() = default;
	Test(int x) : ele(x) {}
	void* operator new(size_t size) = delete;
	void operator delete(void* p) = delete;
private:
	int ele;
};
int main() { 
	Test* t = new Test();    //error:无法调用已删除的函数
	Test* t = ::new Test();   //使用全局作用域的new

	delete t;     //error
	::delete t;    //使用全局作用域的delete
}

3.1.3operator new接口和operator delete接口

标准库定义了operator new函数和operator delete函数重载的8个重载版本。

  1. 如果将operator new 或 operator delete 定义成类的成员函数时,他们是隐式静态的,因为operator new发生在对象构造之前,也就是说此时只能由类去调用,同样operator delete发生在对象析构之后
  2. 调用operator newoperator new[]时,第一个参数类型必须是size_t,且不能有默认实参,它表名存储该对象所需的字节数或存储数组中所有元素所需要的总空间
  3. 不允许重载void* operator new(size_t, void*);

3.2 operator new 内使用malloc

  • 头文件 —— cstdlib

  • 当定义了全局的operator new 和 operator delete 后,这两个函数必须以某种方式执行分配内存与释放内存的操作

void *operator new(size_t size) {
    cout << "operator new" << endl;
    if (void *mem = malloc(size)) {
        reutrn mem;
    } else {
        throw bad_alloc();
    }
}

void operator delete(void *mem) noexcept {
    free(mem);
}

int main() {
	int* a = ::new int(1);  //打印:operator new
}

3.3 placement new——定位new表达式

4 动态数组

4.1 new 和数组

  1. 分配一个数组会得到一个元素类型的指针, 当用new分配一个数组时,我们并未得到一个数组类型的指针,而是的到一个数组元素类型的指针,因此不能对动态数组调用begin或end

  2. 使用初始化器初始化

string *psa = new string[3]{"123", "456", "789"};

string *psa1 = new string[10]();  	//10个空string
int* pia = new int[10]();		   //10个值为0的int
  1. 动态分配一个空数组是合法的:当我们new分配一个大小为0的数组时,new返回一个合法的非空指针。就像尾后指针一样

4.2智能指针和动态数组

4.2.1 通过unique_ptr管理 —— unique_ptr通过默认模板实参调用delete[]

标准库提供了一个可以管理new分配的数组的unique_ptr版本

unique_ptr<int[]> up(new int[10]);
up.release(); 	//自动调用delete[]销毁其指针
//主体模板
template <class T, class D = default_delete<T>> class unique_ptr;
//数组特化模板
template <class T, class D> class unique_ptr<T[],D>;

//当传入的是数组类型时,D的类型为default_delete<T[]>
template <class T> class default_delete;

template <class T> class default_delete<T[]>;
  • 当调用特化的default_delete类创建出的对象时(operator()),如下图,函数体为::delete[](ptr)

image-20240122235640482

4.2.2 通过shared_ptr管理

  • unique_ptr不同,shared_ptr不直接管理动态数组。如果希望使用shared_ptr管理动态数组,必须提供自己定义的删除器
shared_ptr<int> sp(new int[10], [](int *p) {delete[] p;} );
sp.reset();		//使用我们提供的lambda释放数组,它使用delete[]

4.2.2 详细的智能指针删除器区别看第十六章—1.9

4.2 allocator

  • 标准库allocator类定义在头文件memory中,他帮助我们将内存分配和对象构造分离开来。他提供一种类型感知的内存分配方法,他分配的内存是原始的、未构造的

4.2.1 allocator分配未构造的内存

image-20240123112400856

  • deallocate释放内存,在调用deallocate之前,用户必须对每个在这块内存中创建的对象调用destorydestroy只是单纯的调用在改空间上对象的析构

image-20240123113508305

#include <memory>
#include <string>

int main() {
	int n = 3;
	std::allocator<std::string> a;
	auto const p = a.allocate(n);
	auto q = p;

	a.construct(q++);
	a.construct(q++, 10, 'c');
	a.construct(q++, "abc");
	
	while (q != p) {
		a.destroy(--q);
	}
	
	a.deallocate(p, n);
	
}

4.2.2 construct_atdestroy_at——since C++20

C++20之后弃用了constructdestroy 改用construct_atdestroy_at

image-20240123115121988

#include <memory>
#include <string>
#include <iostream>
int main() {
	int n = 1;
	std::allocator<std::string> a;
	auto const p = a.allocate(n);

	std::construct_at(p, 10, 'c');
	std::cout << *p;

	std::destroy_at(p);
	std::cout << *p;   //未定义行为

	a.deallocate(p, n);
}

4.2.3 拷贝和填充未初始化内存的算法

  1. uninitialized_copy
  2. uninitialized_copy_n
  3. uninitialized_fill
  4. uninitialized_fill_n

片转存中…(img-l5DrCSyF-1705985226761)]

#include <memory>
#include <string>
#include <iostream>
int main() {
	int n = 1;
	std::allocator<std::string> a;
	auto const p = a.allocate(n);

	std::construct_at(p, 10, 'c');
	std::cout << *p;

	std::destroy_at(p);
	std::cout << *p;   //未定义行为

	a.deallocate(p, n);
}

4.2.3 拷贝和填充未初始化内存的算法

  1. uninitialized_copy
  2. uninitialized_copy_n
  3. uninitialized_fill
  4. uninitialized_fill_n

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

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

相关文章

电脑文件夹怎么加密?文件夹加密方法介绍

文件夹作为电脑文件管理的重要工具&#xff0c;可以帮助我们有效管理电脑数据。而为了避免数据泄露&#xff0c;我们需要加密文件夹。那么&#xff0c;电脑文件夹该怎么加密呢&#xff1f;下面我们就来了解一下。 方法一&#xff1a;EFS加密 在文件夹上单击鼠标右键&#xff0…

SAP 五个报废率设置简介(上)

通常在生产制造过程中都会面临报废率的问题,生产工艺路线的问题,原材料质量的问题,总会有一些产品在生产过程中被做成报废品,通常报废率的设置有时候会遵循行业的标准设置,亦或者根据工厂生产中统计的历史数据分析后根据不同的产品设置不同的报废率,从而在执行物料的采购…

在Ubuntu上安装pycuda记录

1. 安装CUDA Toolkit 11.8 从MZ小师妹的摸索过程来看&#xff0c;其他版本的会有bug&#xff0c;12.0的版本太高&#xff0c;11.5的太低&#xff08;感谢小师妹让我少走弯路&#xff09; 参考网址&#xff1a;CUDA Toolkit 11.8 Downloads | NVIDIA Developer 在命令行输入命…

Whale 帷幄创始人叶生晅荣获亿欧 2023 中国泛人工智能优秀人物 TOP 20

近日&#xff0c;亿欧在 WIM 2023&#xff08;World Innovators Meet&#xff0c;世界创新者年会&#xff09;上发布 2023 世界创新奖「2023 中国泛人工智能优秀人物 TOP 20」&#xff0c;表彰那些过去一年中在泛人工智能领域做出突出贡献的领导者、开拓者。「Whale 帷幄」创始…

L1-086 斯德哥尔摩火车上的题(Java)

上图是新浪微博上的一则趣闻&#xff0c;是瑞典斯德哥尔摩火车上的一道题&#xff0c;看上去是段伪代码&#xff1a; s a 1112031584 for (i 1; i < length(a); i) {if (a[i] % 2 a[i-1] % 2) {s max(a[i], a[i-1])} } goto_url(www.multisoft.se/ s) 其中字符串的…

LeetCode 热题 100 | 滑动窗口

目录 1 3. 无重复字符的最长子串 2 438. 找到字符串中所有字母异位词 菜鸟做题第二周&#xff0c;语言是 C 1 3. 无重复字符的最长子串 解题思路&#xff1a; 设置两个指针&#xff0c;左指针和右指针&#xff0c;二者之间形成窗口右指针不断右移&#xff0c;新字母被纳…

用通俗易懂的方式讲解:使用 MongoDB 和 Langchain 构建生成型AI聊天机器人

想象一下&#xff1a;你收到了你梦寐以求的礼物&#xff1a;一台非凡的时光机&#xff0c;可以将你带到任何地方、任何时候。 你只有10分钟让它运行&#xff0c;否则它将消失。你拥有一份2000页的PDF&#xff0c;详细介绍了关于这台时光机的一切&#xff1a;它的历史、创造者、…

【一站解决您的问题】mac 利用命令升级nodejs、npm、安装Nodejs的多版本管理器n、nodejs下载地址

一&#xff1a;下载nodejs 官网地址&#xff0c;点击下载稳定版 https://nodejs.org/en 如果官网下载特别慢&#xff0c;可以点击这个地址下载 点击这里 https://nodejs.cn/download/current/ 安装完成后&#xff0c;就包含了nodejs 和 npm。此时您的版本就是下载安装的版本…

慎用“from pwn import *”!和re库findall方法重名引发的问题

今天搓一个sage脚本遇到一个很无语的问题&#xff0c;经过调试&#xff0c;发现是pwntools库中的findall方法和re库中的findall方法重名导致的。这两个findall方法的用法完全不一样&#xff0c;稍有不慎就会踩坑。 文章目录 区分问题引发如何规避 区分 re 是 Python 中用于处理…

使用Python自动化操作手机,自动执行常见任务,例如滑动手势、呼叫、发送短信等等

使用Python自动化操作手机,自动执行常见任务,例如滑动手势、呼叫、发送短信等等。 此自动化脚本将帮助你使用 Python 中的 Android 调试桥 (ADB) 自动化你的智能手机。下面我将展示如何自动执行常见任务,例如滑动手势、呼叫、发送短信等等。 您可以了解有关 ADB 的更多信息,…

C++中的this关键字

2024年1月23日 内容来自 The Cherno:C系列 --------------------------------------------------------------------------------------------------------------------------------- 通过关键字this可以访问成员函数&#xff08;属于某个类的函数&#xff09;&#xff0c;也…

Redis(五)管道

文章目录 官网总结Pipeline与原生批量命令对比Pipeline与事务对比使用Pipeline注意事项 官网 https://redis.io/docs/manual/pipelining/ Pipeline是为了解决RTT往返回时&#xff0c;仅仅是将命令打包一次性发送对整个Redis的执行不造成其它任何影响 总结 Pipeline与原生批量…

从零学习开发一个RISC-V操作系统(四)丨RISC-V汇编语言编程

本篇文章的内容 一、RISC-V汇编语言简介1.1 RISC-V 汇编语言的基本格式1.2 RISC-V 汇编指令操作对象1.3 RISC-V 汇编指令编码格式1.4 RISC-V 汇编指令分类 二、RISC-V汇编语言详解2.1 add 加法指令2.2 sub 减法指令 本系列是博主参考B站课程学习开发一个RISC-V的操作系统的学习…

Leetcode—216.组合总和III【中等】

2023每日刷题&#xff08;七十八&#xff09; Leetcode—216.组合总和III 算法思想 实现代码 class Solution { public:vector<vector<int>> combinationSum3(int k, int n) {vector<vector<int>> ans;vector<int> path;function<void(int,…

Docker初次体验:WSL+Docker+portanier

文章目录 前言Docker是什么&#xff1f;Docker的优点Docker的使用场景&#xff1a;一件安装 Docker安装开启虚拟化安装wsl下载慢的请看这个下载成功 安装Docker修改Docker安装位置 配置Docker安装portanier&#xff08;可视化的Docker操作页面&#xff09;登录网址 总结 前言 …

Git仓库里嵌着别的仓库导致出现问题

例如这样&#xff0c;git仓库里面有箭头&#xff0c;且仓库打开是一个空仓库。 解决问题的方法 1.到子仓库路径下&#xff0c;使用ls -a指令可以看到文件夹下有一个.git文件夹。 我们使用rm -rf <子仓库路径>/.git删除每个子仓库下面的.git文件夹。 2.执行git rm --cache…

ROS:rosdep与ROS2的安装记录

鱼香ROS一键安装&#xff1a; 一键安装微信使用指南 | 鱼香ROS rosdep安装&#xff1a; rosdep 是一个在ROS&#xff08;Robot Operating System&#xff09;生态系统中非常重要的工具&#xff0c;它用于安装ROS包的依赖项。rosdep 确保了ROS包所需的系统依赖项能够被正确安装…

JVM篇----第二篇

系列文章目录 文章目录 系列文章目录前言一、JVM 选项 -XX:+UseCompressedOops 有什么作用?为什么要使用二、怎样通过 Java 程序来判断 JVM 是 32 位 还是 64位?三、32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?四、JRE、JDK、JVM 及 JIT 之间有什么不同?前言 前些天发…

STM32之002--软件安装 Keil

文章目录&#xff1a; 一、安装 Keil 二、注册 三、安装芯片支持包 一、安装 Keil 重点 1&#xff1a; 安装时&#xff0c;不能使用中文路径&#xff0c;否则无法正常使用!! 重点 2&#xff1a; 不要安装 V5.36 及以上的版本&#xff0c;其默认AC6编译器&#xff0c…

PyQt ------ QLineEditor

PyQt ------ QLineEditor 引言正文示例1------基础示例示例2------进阶示例 引言 这里给大家介绍一下 PyQt6 中的 QLineEditor 组件用法。 正文 QLineEditor 是用于做单行字符串输出的组件&#xff0c;它只能够将字符串在一行进行输出&#xff0c;如果要进行多行输出&#x…