智能指针(Newbie Note)

智能指针专题

  • 1.普通指针的问题
  • 2.智能指针是什么
    • 什么是所有权
  • 3.智能指针三个好处:
  • 4.C++11提供的智能指针
    • 4.1 shared_ptr(共享所有权指针)
      • 4.1.1 分配内存
      • 4.1.2 成员函数
      • 4.1.3 计数情况汇总:
      • 4.1.4 示例代码(计数)
      • 4.1.5 示例代码(reset)
      • 4.1.6 自定义删除器
      • 4.1.7 shared_ptr的陷阱
    • 4.2 unique_ptr(独占所有权指针)
      • 4.2.1 分配内存
      • 4.2.2 成员函数
      • 4.2.3 拷贝赋值
      • 4.2.4 release
      • 4.2.5 reset
      • 4.2.6 自定义删除器
      • 4.2.7 unique_ptr的陷阱
        • 不要与裸指针混用
  • 5.性能
  • 6.选择指针
    • 6.1 unique_ptr场景示例:
      • 6.1.1 作为参数
      • 6.1.2 作为返回值
    • 6.2 shared_ptr场景示例:
        • 6.2.1 作为参数
  • 参考资料

1.普通指针的问题

  • 1.访问失败:如果一块内存被多个指针引用,但其中的一个指针释放且其余的指针并不知道,这样的情况下,就发生了访问失败。

  • 2.内存泄漏:从堆中申请了内存后不释放回去,就会引发内存泄漏。

2.智能指针是什么

在构造的时候分配内存,当离开作用域的时候,自动释放分配的内存,这样的话开发人员就可以从手动动态管理内存的繁杂内容中解放出来。

每种智能指针都是以类模板的方式实现的,shared_ptr 也不例外。

什么是所有权

https://blog.csdn.net/weixin_39722329/article/details/96301534

  • 单一所有权:所有权拥有着有义务去释放或转移所有权,同一时刻只会有一个所有权拥有者

  • 共享所有权:和使用裸指针一样,所有权的使用者不必释放内存,引用计数器会负责释放内存,同一时刻可以有多个所有权拥有者。

3.智能指针三个好处:

  • 1.明确资源的所属权

  • 2.避免忘记delete,这种比较容易犯错误

  • 3.更好的处理异常

//函数结束后shared_ptr自动释放内存
void f(){shared_ptr<int> sp(new int(11));//假设抛出了异常,而且在f中未捕获
}//函数结束后ip所指向的内存没有被释放。
void f1(){int* ip = new int(12);//假设delete语句前抛出了异常,而且在f中未捕获delete ip;
}

重要性:1>>2>3

4.C++11提供的智能指针

4.1 shared_ptr(共享所有权指针)

  共享指针shared_ptr是具有共享所有权语义的智能指针。 每当共享指针shared_ptr的最后一个所有者被销毁时,关联对象都将被删除(或关联资源被清除)。

4.1.1 分配内存

std::make_shared

// make_shared<int>分配一块int类型大小的内存,并值初始化为100(返回值是shared_ptr类型,因此可以直接赋值给sp)
shared_ptr<int> sp = std::make_shared<int>(100);

new 接受指针参数的智能指针构造函数是explicit的,直接初始化形式

// 错误! 不会进行隐式转换,类型不符合
shared_ptr<int> sp1 = new int(100);
// 正确,直接初始化调用构造函数
shared_ptr<int> sp2(new int(100000));

4.1.2 成员函数

  • p.get()
    p.get()的返回值就相当于一个裸指针的值,使用遵守以下几个约定
    1.不要保存p.get()的返回值
    2.无论是保存为裸指针还是shared_ptr都是错误的
    3.保存为裸指针不知什么时候就会变成空悬指针
    4.保存为shared_ptr则产生了独立指针
    5.不要delete p.get()的返回值
    6.会导致对一块内存delete两次的错误
  • swap(p,q)
    ​ 交换p、q中保存的指针
  • shared_ptr<T> p(q)
    ​ p是q的拷贝,它们指向同一块内存,互相关联
  • p = q
    ​ 用q为p赋值,之后p、q指向同一块内存,q引用计数+1,p(原来内存空间的)引用计数-1
  • p.use_count()
    ​ 返回与p共享对象的智能指针数量
  • shared_ptr<T> p(q,d)
    ​ q是一个可以转换为T*的指针,d是一个可调用对象(作为删除器),p接管q所指对象的所有权,用删除器d代替delete释放内存
  • p.reset()
    ​ 将p重置为空指针
  • p.reset(p)
    ​ 将p重置为p(的值)
  • p.reset(p,d)
    ​ 将p重置为p(的值)并使用d作为删除器
  • shared_ptr为什么没有release()
    ​ 对于shared_ptr,是可能存在多个shared_ptr指向同一块内存,如果提供了release可能会造成错误的释放,导致其他shared_ptr出现错误。

4.1.3 计数情况汇总:

赋值(增加)

auto sp = make_shared<int>(1024); // sp的引用计数为1
#include <iostream>
#include <memory>using namespace std;
// compile:g++ test.cpp -o a.exe -std=c++11int main()
{{auto sp1 = make_shared<string>("obj1");auto sp2(sp1);auto sp3 = make_shared<string>("obj2");cout << "before sp2->use_count() = " << sp2.use_count() << '\n';cout << "before sp3->use_count() = " << sp3.use_count() << '\n';sp1 = sp3; // 该操作会减少sp2的引用计数,增加sp3的引用计数cout << "after sp2->use_count() = " << sp2.use_count() << '\n';cout << "after sp3->use_count() = " << sp3.use_count() << '\n';}return 0;
}

该操作会减少sp2的引用计数,增加sp3的引用计数。(sp1、sp2指向对象obj1,sp3指向对象obj2,那么赋值之后,sp1也会指向obj2,那就是说指向obj1的就少了,指向obj2的就会多。)

拷贝(增加)

auto sp2 = make_shared<int>(1024);
auto sp1(sp2);

该操作会使得sp1和sp2都指向同一个对象。

传参(拷贝)(增加)
而关于拷贝比较容易忽略的就是作为参数传入函数:

auto sp2 = make_shared<int>(1024);
func(sp2); // func的执行会增加其引用计数

reset(减少)

释放sp指向的对象( 而如果sp是唯一指向该对象的,则该对象被销毁 )

sp.reset()

4.1.4 示例代码(计数)

参考:https://en.cppreference.com/w/cpp/memory/shared_ptr

#include <iostream>
#include <memory>// g++ test.cpp -o a.exe -std=c++11 class Base
{
public:Base() { std::cout << "  Base::Base()\n"; }~Base() { std::cout << "  Base::~Base()\n"; }
};class Derived: public Base
{
public:Derived() { std::cout << "  Derived::Derived()\n"; }~Derived() { std::cout << "  Derived::~Derived()\n"; }
};void test(std::shared_ptr<Base> p)增加引用计数
{   std::cout << "local pointer in a function:\n"<< "  p.get() = " << p.get()<< ", p.use_count() = " << p.use_count() << '\n';std::shared_ptr<Base> lp = p; std::cout << "local pointer in a function:\n"<< "  lp.get() = " << lp.get()<< ", lp.use_count() = " << lp.use_count() << '\n';
}//销毁ptr,减少引用计数int main()
{std::shared_ptr<Base> p = std::make_shared<Derived>();std::cout << "func before:\n"<< "  p.get() = " << p.get()<< ", p.use_count() = " << p.use_count() << '\n';test(p);std::cout << "func after:\n"<< "  p.get() = " << p.get()<< ", p.use_count() = " << p.use_count() << '\n';
}

运行结果

Base::Base()
Derived::Derived()
func before:p.get() = 0x1028c30, p.use_count() = 1
local pointer in a function:p.get() = 0x1028c30, p.use_count() = 2
local pointer in a function:lp.get() = 0x1028c30, lp.use_count() = 3
func after:p.get() = 0x1028c30, p.use_count() = 1
Derived::~Derived()
Base::~Base()

4.1.5 示例代码(reset)

重置共享指针,减少计数

#include<memory>
#include<iostream>
// 编译: g++ test.cpp -o a.exe -std=c++11using namespace std;
class A {
public:int i ;A() { cout << "construct\n"; }~A() { cout << "delete "<< i <<"\n"; }
};
int main()
{// 共享指针a,b,c都指向堆内存new A的位置shared_ptr<A> a( new A);shared_ptr<A> b(a);shared_ptr<A> c(b);shared_ptr<A> d(new A);a->i = 10;cout << a.use_count() << endl;cout << b.use_count() << endl;d->i = 30;// 错:不要用p.get()的返回值为shared_ptr赋值,因为返回的是裸指针,很容易被共享指针重复释放,造成错误//a.reset(d.get())// 令a释放指向的空间 A//a.reset();// 令a释放指向的空间 A,指向新空间a.reset(new A);a->i = 20;cout << b.use_count() << endl;cout << "end" <<endl;return 0;
}

4.1.6 自定义删除器

  如果用shared_ptr管理非new对象或是没有析构函数的类时,应当为其传递合适的删除器,原理是:当删除器的指针Deleter传给shared_ptr/unique_ptr时,shared_ptr/unique_ptr不会使用默认的delete val来释放其管理的资源,而是使用Deleter(val)来释放资源,这样就调用了Deleter来释放管理的资源。

1.普通删除函数定义类似于:

void Deleter(T *val){// 其他代码// 释放val的内存delete val;// 或者(如果val是数组)delete[] val;
}
#include <iostream>
#include <memory>
#include <string>
// 编译: g++ test.cpp -o a.exe -std=c++11
using namespace std;class Connection{
public:string _name;explicit Connection(string name):_name(name){}string get_name() const {return _name;}
};void close(Connection* connection){cout << string("关闭") + connection->get_name() + "管理的连接中..." << endl;// 关闭连接的代码// .....cout << "关闭完成。" << endl;
}// 函数式删除器
void Deleter(Connection *connection){close(connection);delete connection;
}int main(){// 新建管理连接Connection的智能指针shared_ptr<Connection> sp(new Connection("shared_ptr"), Deleter);sp->_name = "hello";
}
  1. 自定义释放规则,用于申请的动态数组

  对于申请的动态数组来说,shared_ptr 指针默认的释放规则是不支持释放数组的,只能自定义对应的释放规则,才能正确地释放申请的堆内存。释放规则可以使用 C++11 标准中提供的 default_delete 模板类,我们也可以自定义释放规则:

//1.指定 default_delete 作为释放规则
std::shared_ptr<int> p6(new int[10], std::default_delete<int[]>());//2.自定义释放规则
void deleteInt(int*p) {delete []p;
}
//初始化智能指针,并自定义释放规则
std::shared_ptr<int> p7(new int[10], deleteInt);//3.lambda方式构造和释放
std::shared_ptr<int> p7(new int[10], [](int* p) {delete[]p; });  

4.1.7 shared_ptr的陷阱

1.不要与裸指针混用
错误场景1:

int *x(new int(10));
shared_ptr<int> sp1(x);
shared_ptr<int> sp2(x);//x随时可能变成空悬指针而无从知晓

错误场景2:

int *x(new int(10));
//创建了一个指向x指针所指内存的共享指针,引用计数为1,是引用这块内存的唯一共享指针func(shared_ptr<int> (x));
//离开函数即离开共享指针的作用域,这块内存即被删除

2.谨慎使用p.get()的返回值

shared_ptr<int> sp1(new int(10));
shared_ptr<int> sp2(sp1), sp3;
sp3 = sp1;//一个典型的错误用法
shared_ptr<int> sp4(sp1.get()); 
cout << sp1.use_count() << " " << sp2.use_count() << " " << sp3.use_count() << " " << sp4.use_count() << endl;//输出:
3  
3 
3  
1(独立)

sp1,sp2,sp3是相互关联的共享指针,共同控制所指内存的生存期,sp4虽然指向同样的内存,却是与sp1,sp2,sp3独立的,sp4按自己的引用计数来关联内存的释放。

4.2 unique_ptr(独占所有权指针)

两个unique_ptr不能指向同一个对象,不能进行复制操作,只能进行移动操作。

4.2.1 分配内存

​ 与shared_ptr不同,unique_ptr没有定义类似make_shared的操作,因此只可以使用new来分配内存,并且由于unique_ptr不可拷贝和赋值,初始化unique_ptr必须使用直接初始化的方式。

unique_ptr<int> up1(new int());    // okay:直接初始化std::unique_ptr<int> p4(new int);
std::unique_ptr<int> p5(std::move(p4)); // okay:调用移动构造函数,p5 将获取 p4 所指堆空间的所有权,而 p4 将变成空指针(nullptr)unique_ptr<int> up2 = new int();   // error! 避免隐式转换
unique_ptr<int> up3(up1);          // error! 不允许拷贝

4.2.2 成员函数

  • up.release()

    ​ up放弃对它所指对象的控制权,并返回保存的指针,将up置为空,不会释放内存

  • up.reset()
    参数可以为空,内置指针,先将up所指对象释放,然后重置up的值

4.2.3 拷贝赋值

  前面说了unique_ptr不可拷贝和赋值,那要怎样传递unique_ptr参数和返回unique_ptr呢? 事实上不能拷贝unique_ptr的规则有一个例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr

// 从函数返回一个unique_ptr
unique_ptr<int> func1(int a)
{return unique_ptr<int> (new int(a));
}// 返回一个局部对象的拷贝
unique_ptr<int> func2(int a)
{unique_ptr<int> up(new int(a));return up;
}

或者是引用

void func1(unique_ptr<int> &up){cout<<*up<<endl;
}unique_ptr<int> func2(unique_ptr<int> up){cout<<*up<<endl;return up;
}// 使用up作为参数
unique_ptr<int> up(new int(10));// 传引用,不拷贝,不涉及所有权的转移
func1(up);// 暂时转移所有权,函数结束时返回拷贝,重新收回所有权
up = func2(unique_ptr<int> (up.release()));
// 如果不用up重新接受func2的返回值,这块内存就泄漏了

4.2.4 release

释放方法:注意!注意!注意!这里的释放并不会摧毁其指向的对象,而且将其指向的对象释放出去。

#include <iostream>
#include <memory>//编译:g++ test.cpp -o a.exe -std=c++11int main () {std::unique_ptr<int> auto_pointer(new int);int * manual_pointer;*auto_pointer=10;manual_pointer = auto_pointer.release();// (auto_pointer is now empty)std::cout << "manual_pointer points to " << *manual_pointer << '\n';delete manual_pointer;return 0;
}

执行结果为:

manual_pointer points to 10

4.2.5 reset

  重置方法,销毁由该智能指针管理的任何可能存在的对象,该智能指针被指为空

#include <iostream>
#include <memory>//编译:g++ test.cpp -o a.exe -std=c++11int main () {std::unique_ptr<int> up;  // emptyup.reset (new int);       // takes ownership of pointer*up=5;std::cout << *up << '\n';up.reset (new int);       // deletes managed object, acquires new pointer*up=10;std::cout << *up << '\n';up.reset();               // deletes managed objectreturn 0;
}

执行结果:

5
10

4.2.6 自定义删除器

#include <iostream>
#include <memory>
#include <string>
// 编译: g++ test.cpp -o a.exe -std=c++11
using namespace std;class Connection{
public:string _name;explicit Connection(string name):_name(name){}string get_name() const {return _name;}
};void close(Connection* connection){cout << string("关闭") + connection->get_name() + "管理的连接中..." << endl;// 关闭连接的代码// .....cout << "关闭完成。" << endl;
}// 函数式删除器
void Deleter(Connection *connection){close(connection);delete connection;
}int main(){// 新建管理连接Connection的智能指针unique_ptr<Connection, decltype(Deleter)*> up(new Connection("unique_ptr"), Deleter);up->_name = "hello";
}

4.2.7 unique_ptr的陷阱

不要与裸指针混用

错误做法

int *x(new int());
unique_ptr<int> up1,up2;
// 会使up1 up2指向同一个内存
up1.reset(x);
up2.reset(x);

  unique_ptr不允许两个独占指针指向同一个对象,在没有裸指针的情况下,我们只能用release获取内存的地址,同时放弃对对象的所有权,这样就有效避免了多个独占指针同时指向一个对象。
正确做法

unique_ptr<int> up1(new int());    // okay,直接初始化
unique_ptr<int> up2;
up2.reset(up1.release());

5.性能

  1. 内存占用高
    shared_ptr 的内存占用是裸指针的两倍。因为除了要管理一个裸指针外,还要维护一个引用计数。
    因此相比于 unique_ptr, shared_ptr 的内存占用更高
  2. 原子操作性能低
    考虑到线程安全问题,引用计数的增减必须是原子操作。而原子操作一般情况下都比非原子操作慢。
  3. 使用移动优化性能
    shared_ptr 在性能上固然是低于 unique_ptr。而通常情况,我们也可以尽量避免 shared_ptr 复制。
    如果,一个 shared_ptr 需要将所有权共享给另外一个新的 shared_ptr,而我们确定在之后的代码中都不再使用这个 shared_ptr,那么这是一个非常鲜明的移动语义。对于此种场景,我们尽量使用 std::move,将 shared_ptr 转移给新的对象。因为移动不用增加引用计数,性能比复制更好。

6.选择指针

不是说任何地方都要使用智能指针,比如说你想传递一个对象到一个函数里,那你就可以使用引用或者普通指针(raw ptr), 这里的引用和普通指针体现的是没有所属权(ownership),也就是说函数本身不负责这个对象的生命周期。只有需要体现所属权(ownership)的创立和变动的时候,采用智能指针。参考

选择条件:
在使用智能指针的时候,优先选用unique_ptr,原因如下:
1.语义简单,即当你不确定使用的指针是不是被分享所有权的时候,默认选unique_ptr独占式所有权,当确定要被分享的时候可以转换成shared_ptr;
2.unique_ptr效率比shared_ptr高,不需要维护引用计数和背后的控制块;
3.unique_ptr用起来更顺畅,选择性更多,可以转换成shared_ptr和通过get和release定制化智能指针(custom smart pointer)。

如果有多个指针指向同一对象的话,你应该使用shared_ptr;

如果一个对象只需要一个智能指针,那你应该是用unique_ptr,它非常适合于返回值类型为unique_ptr的函数

6.1 unique_ptr场景示例:

6.1.1 作为参数

因为不能被拷贝,所以传递裸指针或者引用或者外部不需要了直接转移,但是要注意不能传递值(拷贝)

// 裸指针
#include<iostream>
#include<memory>
void test(int *p)
{*p = 10;
}
int main()
{std::unique_ptr<int> up(new int(42));test(up.get());//传入裸指针作为参数std::cout<<*up<<std::endl;//输出10return 0;
}// 引用
#include<iostream>
#include<memory>
void test(std::unique_ptr<int> &p)
{*p = 10;
}
int main()
{std::unique_ptr<int> up(new int(42));test(up);std::cout<<*up<<std::endl;//输出10return 0;
}// 转移
#include<iostream>
#include<memory>
void test(std::unique_ptr<int> p)
{*p = 10;
}
int main()
{std::unique_ptr<int> up(new int(42));test(std::unique_ptr<int>(up.release()));//test(std::move(up));//这种方式也可以return 0;
}

6.1.2 作为返回值

返回可以用unique_ptr(伪代码),这里可以理解为返回值是拷贝了指向的地方,相当于std::move,获取唯一的所有权

unique_ptr<int> make_init(int n)
{return unique_ptr<int> (new int(n));
}int main()
{
···vector<unique_ptr<int>> vp(size);for(int i = 0; i < vp.size(); i++)vp[i] = make_init(rand() % 1000)
···
}

6.2 shared_ptr场景示例:

6.2.1 作为参数
#include<iostream>
#include<memory>
void func0(std::shared_ptr<int> sp)
{std::cout<<"fun0:"<<sp.use_count()<<std::endl;
}void func1(std::shared_ptr<int> sp)
{std::cout<<"fun1:"<<sp.use_count()<<std::endl;
}void func2(std::shared_ptr<int> &sp)
{std::cout<<"fun1:"<<sp.use_count()<<std::endl;
}int main()
{auto sp = std::make_shared<int>(1024);func0(sp);   // 拷贝方式(这种方式unique不可以)func1(sp);	 // 拷贝方式(这种方式unique不可以)func2(sp);   // 引用方式return 0;
}

这里建议传参使用引用,免拷贝。

参考资料

主要参考:https://www.yanbinghu.com/categories/Cpp/

C++11智能指针: https://www.jianshu.com/p/e4919f1c3a28

C++11 shared_ptr智能指针:http://c.biancheng.net/view/7898.html

shared_ptr官方讲解:http://www.cplusplus.com/reference/memory/shared_ptr/shared_ptr/

unique_ptr官方讲解:http://www.cplusplus.com/reference/memory/unique_ptr/unique_ptr/

智能指针的选择:https://blog.csdn.net/qq_22533607/article/details/82318595

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

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

相关文章

Java深拷贝与浅拷贝技术解析及实例演示

摘要&#xff1a;本文将详细介绍Java中的深拷贝和浅拷贝概念&#xff0c;通过分析源码和举例说明&#xff0c;帮助读者更好地理解这两种拷贝方式的区别及应用场景。 一、深拷贝与浅拷贝的概念 深拷贝&#xff1a;复制一个对象后&#xff0c;无论是基本数据类型还是引用类型&…

多柱汉诺塔问题

k柱汉诺塔 题目描述 汉诺塔&#xff08;Hanoi Tower&#xff09;&#xff0c;又称河内塔。 传说大梵天创造世界的时候做了三根金刚石柱子&#xff0c;按左、中、右排序。大梵天在左侧的柱子上&#xff0c;从下往上按照大小顺序摞着64片黄金圆盘&#xff0c;越靠下的圆盘越大。…

个人博客项目 - 测试报告

文章目录 一、项目背景二、测试报告功能测试1.编写测试用例2.登录测试3.编写文章测试4.查看文章测试5.删除文章测试7.注销登录测试 自动化测试性能测试1.VUG2.进行场景设计3.生成性能测试报告 总结 本文开始 一、项目背景 通过学习测试相关的知识&#xff0c;动手实践并测试一…

2023 年 亚太赛 APMCM ABC题 国际大学生数学建模挑战赛 |数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 以五一杯 A题为例子&#xff0c;以下是咱们做的一些想法呀&am…

【Vue】自定义指令

自定义指令 自定义指令就是自己定义的指令&#xff0c;是对 DOM 元素进行底层操作封装 ,程序化地控制 DOM&#xff0c;拓展额外的功能 全局定义 Vue.directive(指令名字, definition) 指令名&#xff1a;不包括v-前缀&#xff0c;使用时候包括v-&#xff0c;v-指令名defini…

CUTLASS 1.3.3中的 Volta884_h884gemm

CUTLASS 是 CUDA C 模板抽象的集合&#xff0c;用于在 CUDA 内的所有级别和规模上实现高性能矩阵-矩阵乘法 (GEMM) 和相关计算。它采用了类似于 cuBLAS 和 cuDNN 中实现的分层分解和数据移动策略。 CUTLASS 最新版本为3.3&#xff0c;相比1.3.3变动较大。然而重温一下1.3.3仍然…

生产问题 Recv-Q101

生产上服务端口 Recv-Q101 新请求到服务器的失败&#xff0c;幸好及时发现&#xff0c;通过重启服务之后得到解决&#xff0c;具体原因等待排查 目前觉得的原因是&#xff1a;某些请求暂用时间比较久

Linux超简单部署个人博客

1 安装halo 1.1 切换到超级用户 sudo -i 1.2 新建halo文件夹 mkdir ~/halo && cd ~/halo 1.3 编辑docker-compose.yml文件 vim ~/halo/docker-compose.yml 英文输入法下&#xff0c;按 i version: "3"services:halo:image: halohub/halo:2.10container_…

2017年全国硕士研究生入学统一考试管理类专业学位联考数学试题——解析版

文章目录 2017 级考研管理类联考数学真题解析一、问题求解&#xff08;本大题共 5 小题&#xff0c;每小题 3 分&#xff0c;共 45 分&#xff09;下列每题给出 5 个选项中&#xff0c;只有一个是符合要求的&#xff0c;请在答题卡上将所选择的字母涂黑。真题&#xff08;2017-…

Python 提高篇学习笔记(一):深拷贝和浅拷贝

文章目录 一、什么是对象的引用二、深拷贝和浅拷贝2.1 浅拷贝(Shallow Copy)2.2 深拷贝(Deep Copy)2.3 copy.copy和copy.deepcopy的区别 一、什么是对象的引用 在 Python 中&#xff0c;对象的引用是指变量指向内存中某个对象的地址或标识符。当你创建一个新的对象(比如一个整…

[技术杂谈]计算机系统硬件类名称

在各种编程语言都可以见到利用WMI查询计算机硬件信息&#xff0c;因此知道有哪些计算机硬件名称非常有必要&#xff0c;下面列举了所有计算机硬件名称可以查询。 本文内容 冷却设备类输入设备类大容量存储类主板、控制器和端口类 显示另外 6 个 计算机系统硬件类别将表示硬…

git修改远程分支名称

先拉取old_branch最新代码到本地 git checkout old_branchgit pull origin old_branch本地修改后并推送 git branch -m old_branch new_branch # 修改分支名称git push --delete origin old_branch # 删除在远程的老分支推送新分支 git push origin new_branch本地分支与远…

除自身以外数组的乘积[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给你一个整数数组nums&#xff0c;返回数组answer&#xff0c;其中answer[i]等于nums中除nums[i]之外其余各元素的乘积。题目数据保证数组nums之中任意元素的全部前缀元素和后缀的乘积都在32位整数范围内。请不要使用除法&#xff0…

【Qt开发流程】之富文本处理

描述 Scribe框架提供了一组类&#xff0c;用于读取和操作结构化的富文本文档。与Qt中以前的富文本支持不同&#xff0c;新的类集中在QTextDocument类上&#xff0c;而不是原始文本信息。这使开发者能够创建和修改结构化的富文本文档&#xff0c;而不必准备中间标记格式的内容。…

【数据结构】A : A DS图_传递信息

A : A DS图_传递信息 Description 小明在和他的小伙伴们玩传消息游戏&#xff0c;游戏规则如下&#xff1a; 有n名玩家&#xff0c;所有玩家编号分别为0~n-1&#xff0c;其中小明编号为0&#xff1b;每个玩家都有固定的若干个可传信息的其他玩家(也可能没有)。传消息的关系是…

busybox制作根文件系统2

上篇内容使用busybox制作好了根文件系统&#xff0c;接下来需要进行一些测试和功能的完善&#xff01; 根文件系统的测试 测试根文件系统的时候不是直接烧写到EMMC里面&#xff0c;这样测试效率太低了&#xff0c;Ubuntu的rootfs目录已经保存了根文件系统&#xff0c;只需要在…

向量数据库,展望AGI时代

无论是向量数据库&#xff0c;还是大模型&#xff0c;归根结底&#xff0c;大家在追捧它时的心态&#xff0c;焦虑大于需求。 向量数据库的热潮&#xff0c;在一定程度上“外化”了人们的焦虑。 但这并不能否定向量数据库的实际价值&#xff0c;甚至更长远来看&#xff0c;向…

【C++】linux下的gdb程序调试

目录 【C】Linux 下的 GDB 程序调试1. 安装 GDB2. 编译程序3. 启动 GDB4. 设置断点5. 执行程序6. 调试命令7. 调试崩溃8. 结束调试 【C】Linux 下的 GDB 程序调试 在开发 C 程序时&#xff0c;出现 bug 是常见的。调试是找出程序错误的关键步骤之一。在 Linux 环境下&#xff…

RedisTemplate使用详解

RedisTemplate介绍StringRedisTemplate介绍RedisConnectionFactory介绍RedisConnectionFactory源码解析 RedisOperations介绍RedisOperations源码解析 RedisTemplate使用连接池配置RedisTemplate连接池连接池配置 RedisTemplate应用场景RedisTemplate主要特点RedisTemplate使用…

redis运维(十六) 有序集合

一 有序集合 把握一点&#xff1a; 各种redis 命令都提供各种语言对应的API 接口,后续API是关键 ① 概念 1、sorted set --> 有序集合2、redis有序集合也是集合类型的一部分&#xff0c;所以它保留了集合中元素不能重复的特性3、但是不同的是,有序集合给每个元素多设置…