【C++11】三大神器之——智能指针

2023-05-16

文章目录

      • 前言
    • 一、智能指针的原理
      • 1、RAII机制
      • 2、简单的实现
    • 二、智能指针的用法
      • 1、智能指针的分类
      • 2、unique_ptr
        • 基本语法
      • 3、shared_ptr
        • 基本语法
      • 4、删除器
      • 5、weak_ptr

前言

一、智能指针的原理

1、RAII机制

RAII(Resource Acquisition is Initialization),即【资源获取即初始化】,也就是说在构造函数中申请分配资源,在析构函数中释放资源。因为C++的语言机制保证了,当一个对象创建的时候,自动调用构造函数,当对象超出作用域的时候会自动调用析构函数。所以,在RAII的指导下,我们应该使用类来管理资源,将资源和对象的生命周期绑定。

智能指针就是RAII最具代表的实现之一。使用智能指针,可以实现自动的内存管理,再也不需要担心忘记delete造成的内存泄漏。毫不夸张的来讲,有了智能指针,代码中几乎不需要再出现delete了。

2、简单的实现

在了解了RAII机制之后,我们可以尝试实现一个简单的智能指针。

#include <iostream>
using namespace std;
/*RAII技术被认为是C++中管理资源的最佳方法,
进一步引申,使用RAII技术也可以实现安全、
简洁的状态管理,编写出优雅的异常安全的代码。*/
template<class T>
class Smart_ptr
{
private:
    T* m_p;
public:
    //调用构造函数时,申请资源
    Smart_ptr(T* p = nullptr):m_p(p)
    {
         cout << "constructor ptr" << endl;
    }

    //调用析构函数时,自动释放资源
    ~Smart_ptr()
    {
        if(m_p)
        {
            cout << "delete ptr" << endl;
            delete m_p;
        }
    }

    //普通指针的功能
    //通过重载运算符使其拥有和普通指针一样的功能
    T& operator*()const {return *m_p;}
    T* operator->()const {return m_p;}
};

在上述代码中,我们利用RAII机制,实现了智能指针自动释放资源,又通过重载了operator*和operator->,使其具有和指针一样的行为和功能。

我们可以通过以下的测试用例,观察RAII机制下的智能指针的工作情况:

struct AA
{
    int m_a;
    AA(int a = 0):m_a(a){cout << "constructor AA" << endl;}
    ~AA(){cout << "destructor AA" << endl;}
};

int main()
{
	//语句块方便观察析构函数的调用
	//指向自定义类型的对象
    {
        AA* pa = new AA;
        Smart_ptr<AA> sp1(pa);
        cout << sp1->m_a << endl;
    }
	//指向自定义类型的匿名对象
    {
        Smart_ptr<AA> sp2(new AA(5));
        cout << sp2->m_a << endl;
    }
    //指向内置类型的对象
    {
        Smart_ptr<int> sp3(new int(5));
        cout << (*sp3) << endl;
    }
    return 0;
}

运行结果:
constructor AA
constructor ptr
0
delete ptr
destructor AA
constructor AA
constructor ptr
5
delete ptr
destructor AA
constructor ptr
5
delete ptr

二、智能指针的用法

1、智能指针的分类

在C++11中,提供了三种智能指针,接下来我们来逐一了解。使用这些智能指针时需要引用头文件< memory>。

  • std::shared_ptr:共享的智能指针
  • std::unique_ptr:独占的智能指针
  • std::weak_ptr:弱引用的智能指针,它不共享指针,不能操作资源,是用来监视shared_ptr的。

(在C++11之前还有auto_ptr,但由于它并非安全的所以本文不作介绍)

2、unique_ptr

unique_ptr独享它指向的对象,也就是说,同时只有一个unique_ptr指向同一个对象,当这个unique_ptr被销毁时,指向的对象也随即被销毁。

以下是unique_ptr的声明:

template <typename T, typename D = default_delete<T>>//第二个模板参数D:指定删除器,缺省用delete释放资源
class unique_ptr
{
public:
	explicit unique_ptr(pointer p) noexcept;	// 不可用于转换函数。
	~unique_ptr() noexcept;    
	T& operator*() const;            // 重载*操作符。
	T* operator->() const noexcept;  // 重载->操作符。
	unique_ptr(const unique_ptr &) = delete;   // 禁用拷贝构造函数。
	unique_ptr& operator=(const unique_ptr &) = delete;  // 禁用赋值函数。
	unique_ptr(unique_ptr &&) noexcept;	  // 右值引用。
	unique_ptr& operator=(unique_ptr &&) noexcept;  // 右值引用。
	// ...
private:
	pointer ptr;  // 内置的指针。
};

可以看到,unique_ptr禁用了拷贝构造,也就是说不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。

基本语法

(1)初始化
unique_ptr的初始化方法和我们实现的智能指针的类似。事实上,我们可以使用以下的方法:

class AA{};
new* pa = new AA; 
//通过构造函数
std::unique_ptr<AA> ptr1(new AA); // 分配内存并初始化
std::unique_ptr<AA> ptr1(pa);	//将原始指针移交给智能指针管理
//通过移动函数
std::unique_ptr<AA> ptr2 = move(ptr1);
//通过reset初始化
ptr2.reset(new int);

//但以下这些方法是错误的:
// std::unique_ptr<AA> pu1 = p;              // 错误,不能把普通指针直接赋给智能指针。
// std::unique_ptr<AA> pu2 = new AA("hello."); // 错误,不能把普通指针直接赋给智能指针。
// std::unique_ptr<AA> pu3 = pu2;           // 错误,不能用其它unique_ptr拷贝构造。
// std::unique_ptr<AA> pu3;
// pu3 = pu1;                            // 错误,不能用=对unique_ptr进行赋值。

(2)一些技巧

  • 智能指针unique_ptr重载了*和->操作符,使其可以像使用指针一样使用。
  • 用nullptr给unique_ptr赋值将释放对象,空的unique_ptr==nullptr。
  • 使用get()方法返回原始指针。
  • 使用release()释放对原始指针的控制权,将unique_ptr置为空,返回原始指针(可用于把unique_ptr传递给子函数,子函数将负责释放对象)。
  • 使用std::move()可以转移对原始指针的控制权(即允许赋值右值,这点在初始化时提起过)。
  • 使用reset()释放对象:
    1.pp.reset(); // 释放pp对象指向的资源对象
    2.pp.reset(nullptr); // 释放pp对象指向的资源对象
    3.pp.reset(new AA(“bbb”)); // 释放pp指向的资源对象,同时指向新的对象
  • swap()交换两个unique_ptr的控制权。
  • 不支持指针的运算(+、-、++、–)。

注意:unique_ptr也不是绝对安全的,如果程序中调用exit()退出,全局的unique_ptr可以自动释放,但局部的unique_ptr无法释放。

unique_ptr还提供了支持数组的具体化版本,使用如下:

unique_ptr<int[]> parr1(new int[3]);          // 不指定初始值
unique_ptr<int[]> parr1(new int[3]{ 33,22,11 });  // 指定初始值

3、shared_ptr

shared_ptr共享它指向的对象,多个shared_ptr可以指向(关联)相同的对象。在其内部采用计数机制来实现,当新的shared_ptr与对象关联时,引用计数增加1,当shared_ptr超出作用域时,引用计数减1。当引用计数变为0时,则表示没有任何shared_ptr与对象关联,则释放该对象。

基本语法

(1)初始化
和unique_ptr不同的是,shared_ptr没有删除拷贝构造和赋值,并且在C++11标准中可以通过std::make_shared初始化,效率更高(std::make_unique在C++14中才有)。

struct AA{};
//通过构造函数
shared_ptr<AA> pa0(new AA);
//通过移动函数
shared_ptr<AA> pa1 = move(pa0);
//通过拷贝函数
shared_ptr<AA> pa2 = pa1;
//通过std::make_shared(推荐)
shared_ptr<AA> pa3 = std::make_shared<AA>();
//通过reset初始化
pa0.reset(); //重置pa0, 使pa0的引用基数为0
pa0.reset(new int);

此外:

  • 使用use_count()方法返回引用计数器的值。
  • 使用unique()方法,如果use_count()为1,返回true,否则返回false。
  • 不要用同一个原始指针初始化多个shared_ptr。
  • shared_ptr没有release()函数。
  • 其他的函数使用方法和unique_ptr相同。
class AA
{
public:
	string m_name;
	AA() { cout << m_name << "调用构造函数AA()。\n"; }
	AA(const string & name) : m_name(name) { cout << "调用构造函数AA("<< m_name << ")。\n"; }
	~AA() { cout << "调用了析构函数~AA(" << m_name << ")。\n"; }
};

int main()
{
	shared_ptr<AA> pa0(new AA("aa"));     // 初始化资源aa
	shared_ptr<AA> pa1 = pa0;                       // 用已存在的shared_ptr拷贝构造,计数加1
	shared_ptr<AA> pa2 = pa0;                       // 用已存在的shared_ptr拷贝构造,计数加1
	cout << "pa0.use_count()=" << pa0.use_count() << endl;   // 值为3
	
    cout << "pa0.get() = " << pa0.get() << endl;//pa0.get() = 0x1fb131d1420
    cout << "pa1.get() = " << pa1.get() << endl;//pa1.get() = 0x1fb131d1420
    cout << "pa2.get() = " << pa2.get() << endl;//pa2.get() = 0x1fb131d1420

	shared_ptr<AA> pb0 = make_shared<AA>("bb");    // 初始化资源bb
	shared_ptr<AA> pb1 = pb0;                      // 用已存在的shared_ptr拷贝构造,计数加1
	cout << "pb0.use_count()=" << pb0.use_count() << endl;   // 值为2

	cout << "pb0.get() = " << pb0.get() << endl;//pb0.get() = 0x1fb131d17a0
    cout << "pb1.get() = " << pb1.get() << endl;//pb1.get() = 0x1fb131d17a0

	pb1 = pa1;      // 资源aa的引用加1,资源bb的引用减1
	pb0 = pa1;      // 资源aa的引用加1,资源bb的引用成了0,将被释放

	cout << "pa0.use_count()=" << pa0.use_count() << endl;   // 值为5。
	cout << "pb0.use_count()=" << pb0.use_count() << endl;   // 值为5。
}

大家不要误解了共享的含义:shared_ptr指针指向的资源只有一个,它并不是被复制的,而shared_ptr可以有多个。

(2)一些细节

  • 用nullptr给shared_ptr赋值将把计数减1,如果计数为0,将释放对象,空的shared_ptr==nullptr。
  • std::move()可以转移对原始指针的控制权。还可以将unique_ptr转移成shared_ptr。
  • shared_ptr的线程安全性:
    1.shared_ptr的引用计数本身是线程安全(引用计数是原子操作)。
    2.多个线程同时读同一个shared_ptr对象是线程安全的。
    3.如果是多个线程对同一个shared_ptr对象进行读和写,则需要加锁。
    4.多线程读写shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,也需要加锁保护。

4、删除器

在默认情况下,智能指针过期的时候,用delete原始指针释放它管理的资源,程序员可以自定义删除器,改变智能指针释放资源的行为(旨在释放资源的同时能干点其他事情)。

删除器可以是全局函数、仿函数和Lambda表达式,形参为原始指针:

void deletefunc(AA* a) {    // 删除器,普通函数。
	cout << "自定义删除器(全局函数)。\n";
	delete a;
}

struct deleteclass               // 删除器,仿函数。
{
	void operator()(AA* a) {
		cout << "自定义删除器(仿函数)。\n";
		delete a;
	}
};

auto deleterlamb = [](AA* a) {   // 删除器,Lambda表达式。
	cout << "自定义删除器(Lambda)。\n";
	delete a;
};

给shared_ptr指定删除器十分简单,写入函数名即可:

//普通函数版本
shared_ptr<AA> pa1(new AA("aa"), deletefunc);
//仿函数版本
shared_ptr<AA> pa1(new AA("bb"), deleteclass());
//lambda函数版本
shared_ptr<AA> pa1(new AA("cc"), deleterlamb);

而在unique_ptr中会复杂一些:

//普通函数版本
//模板参数用decltype推断会简单一些
unique_ptr<AA,decltype(deletefunc)*> pu1(new AA("aa"), deletefunc);
unique_ptr<AA,void(*)(AA*)> pu1(new AA("aa"), deletefunc);
//仿函数版本
unique_ptr<AA,deleteclass)> pu1(new AA("bb"), deleteclass());
//lambda函数版本
unique_ptr<AA, decltype(deleterlamb)> pu3(new AA("cc"), deleterlamb);

5、weak_ptr

现在有如下一段代码:

#include <iostream> 
#include <memory>
using  namespace std;

class BB;
class AA
{
public:
	string m_name;
	AA() { cout << m_name << "调用构造函数AA()。\n"; }
	AA(const string & name) : m_name(name) { cout << "调用构造函数AA("<< m_name << ")。\n"; }
	~AA() { cout << "调用了析构函数~AA(" << m_name << ")。\n"; }
    shared_ptr<BB> m_p;
};
class BB
{
public:
	string m_name;
	BB() { cout << m_name << "调用构造函数AA()。\n"; }
	BB(const string & name) : m_name(name) { cout << "调用构造函数AA("<< m_name << ")。\n"; }
	~BB() { cout << "调用了析构函数~AA(" << m_name << ")。\n"; }
    shared_ptr<AA> m_p;
};

int main()
{
    shared_ptr<AA> pa = make_shared<AA>("aa");
    shared_ptr<BB> pb = make_shared<BB>("bb");

    pa->m_p = pb;
    pb->m_p = pa;

    cout << "pa.use_count()=" << pa.use_count() << endl;// 结果为2
    cout << "pb.use_count()=" << pb.use_count() << endl;// 结果为2
    return 0;
}

我们会发现shared_ptr的计数器不灵了,因为上述的代码让pa和pb陷入了一个逻辑死区:我等你先死,你等我先死,结果谁都死不了。

为了解决这个问题,C++引入了weak_ptr。weak_ptr 是为了配合shared_ptr而引入的,它指向一个由shared_ptr管理的资源但不影响资源的生命周期。也就是说,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。

不论是否有weak_ptr指向,如果最后一个指向资源的shared_ptr被销毁,资源就会被释放。

我们将上文代码中的类的成员变量指针替换成weak_ptr,结果就一切正常了,大家可以自己试一试。

使用weak_ptr:

  • weak_ptr没有重载 ->和 *操作符,不能直接访问资源。
  • 有以下成员函数:
    1)operator=(); // 把shared_ptr或weak_ptr赋值给weak_ptr
    2)expired(); // 判断它指资源是否已过期(已经被销毁)
    3)lock(); // 返回shared_ptr,如果资源已过期,返回空的shared_ptr
    4)reset(); // 将当前weak_ptr指针置为空
    5)swap(); // 交换

对weak_ptr应用多在多线程中,对此我们总结weak_ptr的灵魂特性:

  • weak_ptr不控制对象的生命周期,但是,它知道对象是否还活着。
  • 用lock()函数把它可以提升为shared_ptr,如果对象还活着,返回有效的shared_ptr,如果对象已经死了,提升会失败,返回一个空的shared_ptr。
  • 提升的行为(lock())是线程安全的。

我们可以通过以下的代码应用这3点:

	shared_ptr<AA> pa = make_shared<AA>("aa");
	{
		shared_ptr<BB> pb = make_shared<BB>("bb");

		pa->m_p = pb;
		pb->m_p = pa;

		shared_ptr<BB> pp = pa->m_p.lock();            // 把weak_ptr提升为shared_ptr。
		if (pp == nullptr)
			cout << "语句块内部:pa->m_p已过期。\n";
		else
			cout << "语句块内部:pp->m_name=" << pp->m_name << endl;
	}
	shared_ptr<BB> pp = pa->m_p.lock();            // 把weak_ptr提升为shared_ptr。
	if (pp == nullptr)
		cout << "语句块外部:pa->m_p已过期。\n";
	else
		cout << "语句块外部:pp->m_name=" << pp->m_name << endl;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C++11】三大神器之——智能指针 的相关文章

  • C++智能指针

    文章目录 前言shared ptrunique ptrweak ptr使用智能指针安全吗 xff1f 前言 下面简单的概括了 3中智能指针的在使用时候需要注意的一些地方 xff0c 三种智能指针在离开其作用域之后都会被自动释放 xff0c
  • 【C++11】三大神器之——智能指针

    文章目录 前言 一 智能指针的原理1 RAII机制2 简单的实现 二 智能指针的用法1 智能指针的分类2 unique ptr基本语法 3 shared ptr基本语法 4 删除器5 weak ptr 前言 一 智能指针的原理 1 RAII
  • 智能指针(shared_ptr,unique_ptr)作为函数参数或者返回值时的一些注意事项

    智能指针 shared ptr unique ptr 作为函数参数或者返回值时的一些注意事项 当智能指针作为函数的参数或者返回值时 xff0c 一直在纠结到底是用智能指针对象本身还是用原始指针 Herb Sutter大师的文章很好的解决了这
  • 面试可能会问六:智能指针

    先看一下百度百科的解释 xff1a 指针指针 当类中有指针成员时 xff0c 一般有两种方式来管理指针成员 xff1a 一是采用值型的方式管理 xff0c 每个类对象都保留一份指针指向的对象的拷贝 xff1b 另一种更优雅的方式是使用智能指
  • stl智能指针和boost智能指针对比

    先说说stl中的智能指针auto ptr xff0c 先说说auto ptr的特点 std auto ptr用来管理单个堆内存对象 xff0c 但是独享所有权 xff0c 且不允许赋值和拷贝 xff08 没有重载operator 61 xf
  • C++11智能指针(六):unique_ptr介绍与例子

    本节介绍下c 43 43 11提供的智能指针实现 std unique ptr lt gt 什么是std unique ptrunique ptr lt gt 是c 43 43 11提供的智能指针实现之一 xff0c 用于防止内存泄漏 un
  • C++智能指针

    参考 xff1a 头文件auto ptr使用示例 unique ptr类模板声明示例 shared ptr模板声明通过辅助类模拟实现 shared ptrshared ptr使用示例 weaked ptrweak ptr 用法weak pt
  • c++11新特性之独占指针unique_ptr

    独占智能指针unique ptr 独占智能指针不允许其他智能指针共享内部的指针 可以通过它的构造函数初始化一个独占智能指针对象 但是不允许通过赋值将一个unique ptr赋值给另一个unique ptr unique ptr不允许复制 但
  • Qt 智能指针详细介绍

    1 Qt智能指针概述 Qt 提供了一套基于父子对象的内存管理机制 所以我们很少需要去手动 delete 但程序中不一定所有类都是QObject的子类 这种情况下仍然需要使用一些智能指针 注意 在 Qt 中使用智能指针时 一定要避免发生多次析
  • C++智能指针 shared_ptr,unique_ptr和weak_ptr

    1 智能指针为什么存在 因为C 没有自动回收内存的机制 因此每一次new出来的动态内存必须手动delete回去 因此智能指针可以解决这个问题 2 智能指针的大致描述是什么 智能指针 自动负责释放所指向的对象 实际上它利用了栈的机制 每一个智
  • 【C++11智能指针】unique_ptr概述、初始化、常用操作、返回unique_ptr、指定删除器、尺寸

    文章目录 1 unique ptr概述 2 unique ptr的初始化 2 1 直接初始化 2 2 make unique函数 3 unique ptr不支持拷贝构造和拷贝赋值 4 unique ptr支持移动构造和移动赋值 5 uniq
  • 智能指针之unique_ptr

    unique ptr实现的是专属所有权语义 用于独占它所指向的资源对象的场合 某个时刻只能有一个unique ptr指向一个动态分配的资源对象 也就是这个资源不会被多个unique ptr对象同时占有 它所管理的资源只能在unique pt
  • 内存的智能管理—智能指针

    前言 我们在使用C和C 进行开发的时候 申请堆区内存是必不可少的 但是很多时候 我们经常忘记释放他导致内存泄漏 从而导致程序崩溃 又或者在尚未使用完成的时候释放 从而导致出现野指针 都是非常危险的现象 所以我们在C 11的标准中提出了智能指
  • 【C++碎碎念】C++11新特性(声明、智能指针、右值引用、lambda表达式)

    目录 一 新类型 二 统一的初始化 三 声明 四 智能指针 五 右值引用 六 Lambda表达式 一 新类型 C 11新增了long long和unsigned long long 以支持64位 或更宽 的整型 新增了类型char16 t
  • C++中智能指针详解

    1 问题引入 在C 中 静态内存和栈内存外 还有一部分内存称为堆程序用堆来存储动态分配的对象即那些在程序运行时分配的对象 当动态对象不再使用时 我们的代码必须显式的销毁它们 在C 中一般使用 new 在动态内存中为对象分配一块空间并返回一个
  • c++智能指针

    智能指针 智能指针也是模版 在头文件
  • c++智能指针

    C 智能指针详解 本文系转载 原文出处 诚然原博主总结的非常好 我只是加一些自己觉得需要补充的地方 并且在最后给出目前c 11在智能指针这方面的弥补 一 简介 由于 C 语言没有自动内存回收机制 程序员每次 new 出来的内存都要手动 de
  • C++智能指针之unique_ptr(保姆级教学)

    目录 unique ptr 概述 涉及程序 初始化 手动初始化 std make unique函数 C 14 unique ptr常规操作 不支持操作 该指针不支持拷贝和赋值操作 所以不能拷贝到容器里 移动语义std move releas
  • C++11智能指针之unique_ptr

    1 智能指针概念 智能指针是基于RAII机制实现的类 模板 具有指针的行为 重载了operator 与operator gt 操作符 可以 智能 地销毁其所指对象 C 11中有unique ptr shared ptr与weak ptr等智
  • C++智能指针作为函数形参和函数返回值的应用场景

    当智能指针作为函数形参时 在调用此函数时 形参代表的智能指针的引用计数将 1 同时退出此函数时 该形参的引用计数将 1 当形参不是智能指针时 调用该函数和退出该函数时 该形参的引用计数不会变化 当函数的内部创建一个智能指针 并需要在函数外使

随机推荐

  • Xshell远程连接华为云服务器

    Xshell远程连接华为云服务器 一 关于华为云1 什么是云服务器2 为什么使用华为云3 我的华为云体验 二 控制台操作 1 设置密码 2 开放端口 3 切换系统 三 Xshell操作 1 下载Xshell和Xftp2 连接云服务器 一 关
  • 校园网网络连接反复断开又连接是什么原因?

    网络连接反复断开又连接是什么原因 xff1f 原因可能跟ARP攻击或擅自使用P2P终结者等攻击软件有关 因为校园内多个楼宇已部署防ARP攻击网络设备 xff0c 只要判断用户计算机感染ARP或使用P2P终结者 网络执法官 聚生网管等软件攻击
  • xuperchain源码分析-启动过程

    xuperchain的启动分为两个比较大的过程 xff0c 一个是节点的初始化 xff0c 另一个是挖坑的初始化
  • 通过Excel学习PID算法(一步步理解它的KP,KI,KD)

    PID原理 PID控制算是应用非常广泛的经典控制算法 但是怎么理解PID这三个参数呢 xff1f 在参考了别人的文章之后 xff0c 我还是有点一知半解 xff0c 这时候发现不自己动手算一算是很难理解PID了 xff0c 但是我又不想做这
  • 通过Excel学习PID算法(连续系统的PID)

    总结上一节 在之前 xff0c 我们用倒水的例子通俗易懂的解释了什么是PID算法 在这里先回顾一下之前的学习的内容 P表示对误差的比例系数 与目标值差多少 xff0c 就在下一次修正中加上这个误差与P的乘积 xff0c 同时会导致系统有一个
  • 原来学习是如此地苦涩

    原文链接 xff1a http blog csdn net tangl 99 article details 2047657 最近一直在忙第一篇Paper xff0c 虽然想法大致的框架成熟了 xff0c 但是还有一些细节需要完善 这几天在
  • 互联网+时代的7个引爆点(读书笔记)

    百货商场里的销售人员一直抱怨 xff0c 大家只是到自己这里来看看 xff0c 之后转身就在网上下单 从旧视角瞎看这固然是一种文体 xff0c 显示着揭示了一种新的机会 以线下体验为入口的机会 小团队精益式的迭代 xff0c 几个周期后就可
  • maperuce运算框架

    1 xff0c 概念 mapreduce 运算框架主要实现hadoop 的数据处理 xff0c 数据处理中 流经过5个节点 数据流 xff1a input gt spilt gt map gt shuffle gt reduce xff08
  • 在Python中使用print输出时,出现UnicodeEncodeError错误,错误提示为“‘gbk‘ codec can‘t encode character ‘\u2022‘ in posit

    利用chatgpt一步步解决了这个问题 xff0c 感觉ChatGPT还是太强大了 问题描述 xff1a 在Python中使用print输出时 xff0c 出现UnicodeEncodeError错误 xff0c 错误提示为 39 gbk
  • openstack一些特性资料

    Keystone RBAC nova compute Cells Bare Metal Compute 是什么东西 xff1f http wiki openstack org blueprint nova compute cells htt
  • 【神经网络和深度学习-开发案例】 第二章 神经网络结构

    神经网络和深度学习 第二章 神经网络结构 案例 xff1a 使用神经网络识别手写数字 我将介绍一个神经网络 xff0c 它可以很好地对手写的数字进行分类 为了准备这一点 xff0c 它有助于解释一些术语 xff0c 让我们可以命名一个网络的
  • 2000页kubernetes操作手册,内容详细代码清晰,小白也能看懂

    现如今 xff0c Kubernetes业务已成长为新时代的IT基础设施 xff0c 并成为高级运维工程师 架构师 后端开发工程师的必修技术栈 毫无疑问 xff0c Kubernetes是云计算发展演进的一次彻底革命性的突破 xff0c 只
  • FreeRTOS代码阅读笔记:heap_4.c

    FreeRTOS中对于内存的管理当前一共有5种实现方式 xff08 作者当前的版本是10 1 1 xff09 xff0c 均在 Source portable MemMang 下面 xff0c 这里笔记下 heap 4 c和第二种方式比较相
  • (1)touchgfx 添加时钟控件

    第一步 xff1a 新建空白模版 添加图片 xff1a 放入 链接 xff1a https pan baidu com s 1NI6LUYrTUs64Z2jZE6AAQQ 提取码 xff1a 2odw 添加控件 xff1a 位置部件属性1T
  • 【基于51】红外寻迹智能小车 - 代码篇

    文章目录 前言一 准备工作二 使用步骤1 模块化编程2 电机模块3 小车动作模块4 PWM 和定时器 中断系统5 寻迹逻辑 总结 前言 关于硬件部分可以看我上次写的帖子https blog csdn net ZER00000001 arti
  • C++关键字override

    一 什么是override override的翻译是覆盖 实际上它在C 43 43 中可以检测哪些虚函数没有被重写并报错 注 xff1a 在派生类的成员函数中使用override时 xff0c 如果基类中无此函数 xff0c 或基类中的函数
  • 邻接矩阵和邻接表

    图的概述和存储结构 xff08 一 xff09 文章目录 前言一 图的概述1 xff09 图的分类2 xff09 图的要素 二 图的存储结构三 邻接矩阵四 邻接表 前言 有一种说法是程序是由数据结构和算法组成的 xff0c 这很能体现出数据
  • 图解迪杰斯特拉(Dijkstra)最短路径算法

    往期文章目录 干货满满 xff01 最小生成树 Prim算法 最小生成树 Kruskal算法 目录 前言 一 最短路径的概念及应用 二 Dijkstra迪杰斯特拉 1 什么是Dijkstra 2 逻辑实现 总结 前言 无论是什么程序都要和数
  • Vscode配置Git+快速入门,一篇学会80%的Git操作

    前言 团队开发中经常会用到Git xff0c 能极大简化开发的流程 xff0c 而个人开发也可以利用Git管理自己的代码 同样作为一个初学者 xff0c 我在学完Git之后写下这篇文章总结个人走过的坑 xff0c 大家一起进步 Git下载和
  • 【C++11】三大神器之——智能指针

    文章目录 前言 一 智能指针的原理1 RAII机制2 简单的实现 二 智能指针的用法1 智能指针的分类2 unique ptr基本语法 3 shared ptr基本语法 4 删除器5 weak ptr 前言 一 智能指针的原理 1 RAII