RAII与智能指针

RAII与智能指针

  • 1.RAII
    • 1.1RAII理解
    • 1.2RAII的原理
      • 1.2.1简单的例子说明局部对象的自动销毁的特性
    • 1.2.2 RAII 过程
  • 2.智能指针
    • 2.1 auto_ptr
      • 2.1.1auto_ptr的使用
        • 构造函数与析构函数
        • 拷贝构造函数与赋值
        • 提领操作
        • auto ptr其它函数
      • 2.1.2autoptr使用的注意事项
    • 2.2 unique_ptr
      • 2.2.1unique_ptr的使用
        • unique_ptr的创建
        • unique_ptr不能进行拷贝构造和赋值操作
        • unique_ptr可以进行移动构造和移动赋值操作
        • unique_ptr虽然没有拷贝操作,但是可以从函数中返回unique_ptr
      • 2.2.2unique_ptr使用场景
    • 2.3 shared_ptr(共享指针)
      • 2.3.1 shared_ptr的使用
        • shared_ptr的创建
        • shared_ptr的访问
        • shared_ptr的拷贝和赋值操作
        • shared_ptr的引用计数
      • 2.3.2 shared_ptr的线程安全
      • 2.3.3 shared_ptr与unordered map使用
    • 2.4 weak_ptr
      • 2.4.1 weak_ptr理解
      • 2.4.2 为什么会有weak_ptr
      • 2.3.3 weak_ptr使用
        • 创建weak_ptr实例
        • 判断weak_ptr指向对象是否存在
        • weak_ptr使用
      • 2.5 几种智能指针的比较

1.RAII

1.1RAII理解

RAII(Resource Acquisition ls lnitialization) ,资源获取即初始化,是由 c++之父 Biarne Stroustrup 提出的。
使用局部对象管理资源的技术称为资源获取即初始化,这里的资源主要是指操作系统中有限的东西如内存、网络套接字,互斥量,文件句柄等等,局部对象是指存储在栈的对象,它的生命周期是由操作系统来管理的,无需人工介入。

1.2RAII的原理

资源的使用一般经历三个步骤:

  1. 获取资源(创建对象),
  2. 使用资源,
  3. 销毁资源 (析构对象)。

但是资源的销毁往往是程序员经常忘记的一个环节,所以程序界就想如何在程序员中让资源自动销毁呢?解决问题的方案是: RAIl,它充分的利用了 C++语言局部对象自动销毁的特性来控制资源的生命周期。

1.2.1简单的例子说明局部对象的自动销毁的特性

#include <iostream>
#include <string>using namespace std;class Student
{
private:const string s_name;int s_age;
public:Student(const string name = "",int age = 0):s_name(name),s_age(age){cout<<"Construct a Student"<<endl;}~Student(){cout<<"Destory a Student"<<endl;}
};int main(){Student stu1; #局部对象return 0;
}

在这里插入图片描述
从 Student类可以看出,当我们在 main 函数中声明一个局部对象的时候,会自动调用构造函数进行对象的初始化,当整个 main 函数执行完成后,自动调用析构函数来销毁对象,整个过程无需人工介入,由操作系统自动完成。
于是,基于上述实现方式,可以想到,当我们在使用资源的时候,在构造函数中进行初始化,在析构函数中进行销毁

1.2.2 RAII 过程

  1. 设计一个类封装资源
  2. 在构造函数中初始化
  3. 在析构函数中执行销毁操作
  4. 使用时声明一个该类的对象

2.智能指针

智能指针其实是将指针进行了封装,可以像普通指针一样进行使用,同时可以自行进行释放,避免忘记释放指针指向的内存地址造成内存泄漏。

C11 里面的四个智能指针:

  1. auto ptr,
  2. unique ptr,
  3. shared ptr,
  4. weak ptr
    下面对这四个智能指针进行一一介绍:

2.1 auto_ptr

auto_ptr是较早版本的智能指针,已经被 C11 弃用,C98 中 auto_ptr 所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理。

2.1.1auto_ptr的使用

构造函数与析构函数

auto_ptr 在构造时获取对某个对象的所有权(ownership),在析构时释放该对象,提高代码安全性,因为我们不必关系应该如何释放auto ptr,也不用担心发生异常时会有内存泄漏。

int *p = new int(10);auto_ptr<int> ap(p);

注意点

  1. auto ptr 析构的时候会删除他所拥有的那个对象,所以我们要注意两个auto_ptr 不能同时拥有同一个对象。
int *p = new int(10);auto_ptr<int> ap1(p);
auto_ptr<int> ap2(p);

因为 ap1 与 ap2 都认为指针 p 是归它管的,在析构时都试图删除 p,两次删除同一个对象的行为在C++标准中是未定义的。所以我们必须防止这样使用 auto ptr

  1. 不应该用 auto ptr 来管理一个数组指针
string * sar = new string[10];auto_ptr<string> ap3(sar);
  1. 构造函数的 explicit 关键词有效阻止从一个“裸指针隐式转换成 auto ptr 类型
拷贝构造函数与赋值

auto ptr 要求其对“裸”指针的完全占有性。也就是说一个“裸”指针不能同时被两个以上的 auto ptr 所拥有。那么,在拷贝构造或赋值操作时,就必须作特殊的处理来保证这个特性。
auto ptr的做法是“所有权转移”,即拷贝或赋值的源对象将失去对“裸”指针的所有权,所以,与一般拷贝构造函数,赋值承数不同,auto ptr 的拷贝构造函数,赋值函数的参数为引用而不是常引用(const reference).
当然,一个 auto ptr 也不能同时拥有两个以上的“裸”指针,所以,拷贝或赋值的目标指针将先释放其所拥有的对象,然后管理旧指针的资源,因此旧指针即指向nullptr,在访问旧指针时会出现悬空现象
注意点:

  1. auto ptr 被拷贝或被赋值后,其已经失去对原对象的所有权,这个时候,对这个 auto ptr 的提领(dereference)操作是不安全的。
int *p = new int(10);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2 = ap1;cout<<*ap1<<endl; //error,此时ap1已经失去对p指针的拥有权
  1. 将 auto_ptr 作为函数参数按值传递时,函数调用过程中在函数的作用域中会产生一个局部对象来接收传入的 auto ptr(拷贝构造),这样,传入的实参 auto ptr 就失去了其对原对象的所有权,而该对象会在函数退出时被局部 auto ptr 删除。
void fun(auto_ptr<int> ap)
{cout<< *ap<< endl;
}int main(){auto_ptr<int> ap1(new int(10));fun(ap1);cout<<*ap1<<endl; //error,经过fun(ap1)函数调用,ap1已经不再拥有任何对象了return 0;
}

这种情况容易出错,所以 auto ptr 作为函数参数按值传递是一定要避免的。或许用auto ptr 的指针或引用作为函数参数或许可以,但是我们并不知道在函数中对传入的 auto ptr 做了什么,如果当中某些操作使其失去了对对象的所有权,那么这还是可能会导致致命的执行期错误。也许用 const reference 的形式来传递 auto ptr 会是一个不错的选择。

  1. auto ptr在基类和子类隐式转换
class Object{ };class Base: public Object{ };auto_ptr<Object> apobj = auto_ptr<Base>(new Base);
提领操作

提领操作有两个操作:

  1. 返回其所拥有的对象的引用,
  2. 实现了通过 auto ptr 调用其所拥有的对象的成员(首先要确保这个智能指针确实拥有某个对象,否则,这个操作的行为即对空指针的提领是未定义)。
auto ptr其它函数
  1. get 用来显式的返回 auto ptr 所拥有的对象指针。我们可以发现,标准库提供的 auto ptr 既不提供从裸”指针到auto _ptr 的隐式转换(构造函数为 explicit),也不提供 auto ptr 到指针的隐式转换,从使用上来讲可能不那么的灵活,考虑到其所带来的安全性还是值得的。
  2. release,用来转移所有权。
  3. reset,用来接收所有权,如果接收所有权的 auto ptr 如果已经拥有某对象,必须先释放该对象.

2.1.2autoptr使用的注意事项

  1. auto ptr 不能指向数组
  2. auto_ptr 不能共享所有权
  3. auto_ptr 不能通过复制操作来初始化
  4. auto_ptr 不能放入容器中使用
  5. auto ptr 不能作为容器的成员

2.2 unique_ptr

C11 中使用 unique_ptr 替代auto_ptr
unique 是独特的、唯一的意思,故名思议,unique ptr 可以“独占”地拥有它所指向的对象,是一种定义在<memory中的智能指针(smart pointer),保证一个对象同一时间只有一个智能指针。
unique_ptr 对象中保存指向某个对象的指针,当它本身被删除或者离开其作用域时会自动释放其指向对象所占用的资源。

2.2.1unique_ptr的使用

unique_ptr的创建

要想创建一个 unique ptr,需要将一个 new 操作符返回的指针传递给 unique ptr 的构造函数.

int main()
{    unique_ptr<int> pt(new int(10));cout<< *pt<< endl;  //10return 0;
}
unique_ptr不能进行拷贝构造和赋值操作
unique_ptr& operator=( const unique_ptr& ) = delete;

在这里插入图片描述

unique_ptr可以进行移动构造和移动赋值操作

unique ptr 虽然没有支持普通的拷贝和赋值操作,但却提供了一种移动机制来将指针的所有权从一个 unique ptr转移给另一个 unique ptr。如果需要转移所有权,可以使用 std:.move()函数

int main(){unique_ptr<int> pt1(new int(10));cout<< *pt1<< endl;  //10unique_ptr<int> pt2(move(pt1)); cout<<"--------------"<<endl;//cout<< *pt1<< endl;  //出错,为空cout<< *pt2<< endl;  //10unique_ptr<int> pt3 = move(pt2); cout<<"~~~~~~~~~~~~~~~~"<<endl;//cout<< *pt1<< endl;  //出错,为空//cout<< *pt2<< endl;  //出错,为空cout<< *pt3<< endl;  //10return 0;
}

在这里插入图片描述

unique_ptr虽然没有拷贝操作,但是可以从函数中返回unique_ptr
unique_ptr<int> clone(int a)
{unique_ptr<int> ptr(new int(a));return ptr;//返回unique_ptr
}int main()
{int val = 11;unique_ptr<int> pt1 = clone(val);cout<< *pt1<< endl;  //11return 0;
}

2.2.2unique_ptr使用场景

  1. 为动态申请的资源提供异常安全保证
void fun_(int a)
{int *p = new int(5);//.....(抛出异常)
}void fun(int a)
{unique_ptr<int> ptr(new int(a));//.....(抛出异常)}

fun_是传统的写法:在动态申请内存后,有可能接下来的代码由于抛出异常或者提前退出 (if 语句)而没有执行 delete 操作。
解决的方法是使用 unique ptr 来管理动态内存,只要 unique ptr 指针创建成功,其析构函数都会被调用。确保动态资源被释放。
2. 返回函数内动态申请资源的所有权

unique_ptr<int> fun(int a)
{unique_ptr<int> ptr(new int(a));return ptr;//返回unique_ptr
}int main()
{int a = 12;unique_ptr<int> ret = fun(a);cout<< *ret<< endl; //12//函数结束后,自动释放资源return 0;
}
  1. 在容器中保存指针
int main()
{vector<unique_ptr<int>> vec;unique_ptr<int> pt(new int(5));vec.push_back(move(pt));for(int i = 0;i < vec.size();i++){cout<< *vec[i]<< endl;  // 5}return 0;
}
  1. 管理动态数组
int main()
{   unique_ptr<vector<int>[]> pt(new vector<int> {1,2,3,4,5});for(int i = 0;i < pt.get()->size();i++){cout<<pt.get()->at(i)<<endl;    //1,2,3,4,5}return 0;
}

2.3 shared_ptr(共享指针)

shared ptr 是一个引用计数的智能指针,用于共享对象的所有权。也就是说它允许多个指针指向同一个对象,并且维护了一个共享的引用计数器,当这个对象所有的智能指针被销毁时(引用计数器==0)就会自动进行回收。

class Object
{
private:int val;
public:Object(int x = 0):val(0){cout<< "Construct Object"<< endl;}~Object(){cout<< "Destory Object"<< endl;}};int main()
{shared_ptr<Object> pobj(new Object(13));//指针引用对象的个数cout<< " pobj:"<< pobj.use_count()<<endl; //1shared_ptr<Object> pobj1 = pobj;cout<< " pobj:"<< pobj.use_count()<<endl; //2cout<< " pobj1:"<< pobj1.use_count()<<endl; //2return 0;
}

在这里插入图片描述
一方面,跟 STL 中大多数容器类型一样,shared ptr 也是模板类,因此在创建 shared ptr 时需要指定其指向的类型。另一方面,shared ptr 指针允许让多个该类型的指针共享同一堆分配对象。同时 shared ptr 使用经典的“引用计数”方法来管理对象资源,每个 shared ptr 对象关联一个共享的引用计数。

2.3.1 shared_ptr的使用

shared_ptr的创建
  1. 调用 make shared 库函数,该函数会在堆中分配一个对象并初始化,最后返回指向此对象的share ptr 实例(安全、高效
  2. 先 new 出一个对象,然后把其原始指针传递给 share ptr 的构造函数
shared_ptr<int> ptm = make_shared<int>(16);cout<< *ptm<< endl; //16shared_ptr<int> ptn(new int(15));cout<< *ptn<< endl; //15
shared_ptr的访问
  1. 解引用操作符*获得原始对象进而访问其各个成员,
  2. 指针访问符->来访问原始对象的各个成员。
shared_ptr的拷贝和赋值操作

对于 shared ptr 在拷贝和赋值时的行为是,每个 shared ptr 都有一个关联的计数值,通常称为引用计数。无论何时我们拷贝一个 shared ptr,计数器都会加1。当我们将一个指针的对象交给另一个指针管理后,其关联的引用计数就会减1。
例如,当用一个 shared ptr 初始化另一个 shred ptr时,或将它当做参数传递给一个函数以及作为函数的返回值时它所关联的计数器就会递增。
当我们给 shared ptr 赋予一个新值或是 shared ptr 被销(例如一个局部的 shared ptr 离开其作用域)时,计数器就会递减。
shared ptr 对象的计数器变为 0,它就会自动释放自己所管理的对象。

class Object
{
private:int val;
public:Object(int x = 0):val(0){cout<< "Construct Object"<< endl;}~Object(){cout<< "Destory Object"<< endl;}};int main()
{shared_ptr<Object> pobj = make_shared<Object>(13);//指针引用对象的个数cout<< " pobj:"<< pobj.use_count()<<endl; //1shared_ptr<Object> pobj1 = pobj;cout<< " pobj:"<< pobj.use_count()<<endl; //2cout<< " pobj1:"<< pobj1.use_count()<<endl; //2return 0;
}

对比我们上面的代码可以看到: 接下来,我们用 pob初始化 pobit1,两者关联的引用计数值增加为 2。随后,函数结束,pObi和 PObi2 相继离开函数作用域,相应的引用计数值分别自减 1 最后变为 0,于是 Obiect 对象被自动释放(调用其析构函数)。

shared_ptr的引用计数

shared _ptr 提供了两个函数来检查其共享的引用计数值,分别是

  1. unique()函数用来测试该 shared ptr 是否是原始指针唯一拥有者,也就是 use count()的返回值为 1时unique()返回 true,否则返回 false。
  2. use count()函数,该函数返回当前指针的引用计数值。值得注意的是 use count()函数能效率很低,应该只把它用于测试或调试。

2.3.2 shared_ptr的线程安全

  1. (shared_ptr) 的引用计数本身是线程安全(引用计数是原子操作)
  2. 多个线程同时读同一个 shared ptr 对象是线程安全的。
  3. 如果是多个线程对同一个 shared ptr 对象进行读和写,则需要加锁
  4. 多线程读写 shared_ptr 所指向的同一个对象,不管是相同的 shared ptr 对象,还是不同的 shared ptr 对象,也需要加锁保护。

2.3.3 shared_ptr与unordered map使用

如果把 shared_ptr 放到 unordered set 中,或者用于 unrdered map 的 key, 那么要小心 hash table 退化为链表。但是其 hash value 是 shared_ptr 隐式转换为 bool 的结果。也就是说,如果不自定义 hash 函数,那么unordered {set/map] 会退化为链表。
为什么要尽量使用 make shared()? 申请被管理对象以及引用计数的内存:调用适当的构造函数初始化对象,返回shared_ptr。
为了节省一次内存分配,原来 shared_ptr x(new Obiect (10) ; 需要为 Obiect对象 和 RefCnt 各分配次内存,现在用 make shared() 的话,可以一次分配一块足够大的内存,供 Obiect 和 RefCnt 对象容身。不过Obiect 的构造函数所需的参数要传给 make shared(),后者再传给 Obiect; : Obiect),这只有在 C++11 里通过perfect forwarding(完美转发) 才能完美解决。

2.4 weak_ptr

2.4.1 weak_ptr理解

weak_ptr 是为了配合 shared ptr 而引入的一种智能指针,它指向一个由 shared ptr 管理的对象而不影响所指对象的生命周期,也就是将一个 weak_ptr 绑定到一个 shared_ptr 不会改变 shared_ ptr 的引用计数
即weak_ptr是为了协助shared_ptr而出现的。它不能访问对象,只能观测shared_ptr的引用计数,防止出现死锁。
不论是否有 weak ptr 指向,一旦最后一个指向对象的 shared_ptr 被销毁,对象就会被释放。从这个角度看weak_ptr 更像是 shared_ptr 的一个助手而不是智能指针。

2.4.2 为什么会有weak_ptr

在出现了循环引用(或环形引用)的情况下,shared_ptr。

class Child;class Parent
{
public:shared_ptr<Child> child;~Parent(){cout<< "Destory Parent"<< endl;}void Priant() const{cout<< "Parent-----------"<< endl;}
};class Child
{
public:shared_ptr<Parent> parent;~Child(){cout<< "Destory Child"<< endl;}
};int main()
{shared_ptr<Parent> parent = make_shared<Parent>();shared_ptr<Child> child = make_shared<Child>();parent->child = child;child->parent = parent;child->parent->Priant();return 0;
}

在这里插入图片描述
上面代码的运行结果,只打印出”Parent-----------”,而并没有打印出"Destory Parent”或”Destory Child",说明 Parent 和 Child 的析构函数并没有调用到。这是因为Parent 和 Child 对象内部,具有各自指向对方的 shared_ptr,加上 parent和 child 这两个shared_ptr,说明每个对象的引用计数都是 2。当程序退出时,即使 parent 和 child 被销毁,也仅仅是导致引用计数变为了1,因此并未销毁 Parent 和 Child 对象。

2.3.3 weak_ptr使用

创建weak_ptr实例

创建一个weak_ptr时,需要用一个 shared_ptr 实例来初始化weak_ptr,由于是弱共享,weak_ptr的创建并不会影响 shared_ptr 的引用计数值

int main()
{shared_ptr<int> sp = make_shared<int>(5);cout<< sp.use_count()<< endl; //1weak_ptr<int> wp(sp);cout<< sp.use_count()<< endl; //1cout<< wp.use_count()<< endl; //1return 0;
}
判断weak_ptr指向对象是否存在

weak_ptr 并不改变其所共享的 shared_ptr 实例的用计数,那就可能存在 weak ptr 指向的对象被释放掉这种情况。这时,我们就不能使用 weak ptr 直接访问对象。那么我们如何判断 weak ptr 指向对象是否存在呢?C++中提供了 lock 函数来实现该功能。
如果对象存在,lock(函数返回一个指向共享对象的 shared ptr,否则返回一个空 shared ptr。

shared_ptr<int> sp = make_shared<int>(5);//sp = nullptr;weak_ptr<int> wp(sp);if(shared_ptr<int> pa = wp.lock()){cout<< *pa <<endl;
}
else{cout<< wp.expired()<< endl; weak_ptr 还提供了 expired0函数来判断所指对象是否已经被销毁cout<< "*wp is nullptr"<< endl;
}
weak_ptr使用

weak_ptr 并没有重载 operator->和 operator 操作符,因此不可直接通过 weak_ptr 使用对象,典型的用法是调用其lock 函数来获得 shared_ptr 示例,进而访问原始对象。
最后,我们来看看如何使用 weak_ptr 来改造最前面的代码,打破循环引用问题。

class Child;class Parent
{
public:weak_ptr<Child> child;~Parent(){cout<< "Destory Parent"<< endl;}void Priant() {cout<< "Parent-----------"<< endl;}
};class Child
{
public:weak_ptr<Parent> parent;~Child(){cout<< "Destory Child"<< endl;}
};int main()
{shared_ptr<Parent> parent1 = make_shared<Parent>();shared_ptr<Child> child1 = make_shared<Child>();weak_ptr<Parent> parent(parent1);weak_ptr<Child> child(child1);parent1->child = child1;child1->parent = parent1;//child1->parent.lock()->Priant();if(!child.expired()){child1->parent.lock()->Priant();}return 0;
}

2.5 几种智能指针的比较

unique_ptr和 shared ptr 类型指针有很大的不同: shared ptr 允许多个指针指同一对象,而 nique_ptr 在某时刻只能有一个指针指向该对象(两个 unique ptr 不能指向同一个对象)。

  1. 使用场景:
    如果程序要使用多个指向同一个对象的指针,应该选择 shared_ptr;
    如果程序要使用一个指向一个对象的指针,则可以使用 unique_ptr;
    如果使用 new [] 分配内存,应该选择 unique_ptr;
    如果函数使用 new 分配内存,并返回指向该内存的指针,将其返回类型声明为 unique_ptr 是不错的选择。

  2. 智能指针实现原理:建立所有权(ownership)概念。
    auto_ptr 和 unique_ptr 的策略:对于特定的对象,只能有一个智能指针可拥有它,这样只有拥有对象的智能指针的析构函数会删除该对象。然后,让赋值操作转让所有权。但 unique_ptr 的策略更严格,unique_ptr 能够在编译期识别错误。
    shared_ptr 采用的策略。:跟踪引用特定对象的智能指针计数,这称为引用计数(reference counting)。例如,赋值时,计数将加 1,而指针过期时,计数将减 1. 仅当最后一个指针过期时,才调用 delete。

  3. 线程安全:
    shared_ptr:引用计数在手段上使用了 atomic 原子操作,只要 shared_ptr 在拷贝或赋值时增加引用,析构时减少引用就可以了。首先原子是线程安全的,所有 shared_ptr 智能指针在多线程下引用计数也是安全的,也就是说 shared_ptr 智能指针在多线程下传递使用时引用计数是不会有线程安全问题的。 但是指向对象的指针不是线程安全的,使用 shared_ptr 智能指针访问资源不是线程安全的,需要手动加锁解锁。智能指针的拷贝也不是线程安全的。

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

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

相关文章

ros学习笔记(1)Mac本地安装虚拟机,安装Ros2环境

Ros与Linux的关系 Ros环境基于Linux系统内核 我们平时用的是Linux发行版&#xff0c;centos&#xff0c;ubuntu等等&#xff0c;机器人就用了ubunut 有时候我们经常会听到ubunue的版本&#xff0c;众多版本中&#xff0c;有一些是长期维护版TLS&#xff0c;有一些是短期维护…

微信小程序个人账号申请和配置详细教程

一、注册小程序管理账号 1、注册方法 在微信公众平台官网首页&#xff08;mp.weixin.qq.com&#xff09;&#xff0c;点击右上角的“立即注册”按钮。 2、选择注册的账号类型 选择“小程序”&#xff0c;点击“查看类型区别”可查看不同类型账号的区别和优势。 3、填写邮箱和…

探秘网页打开的完整过程:DNS解析、CDN加速和Nginx负载均衡的协同驱动

浅谈一个网页打开的全过程&#xff08;涉及DNS、CDN、Nginx负载均衡等&#xff09; 1、概要 从用户在浏览器输入域名开始&#xff0c;到web页面加载完毕&#xff0c;这是一个说复杂不复杂&#xff0c;说简单不简单的过程&#xff0c;下文暂且把这个过程称作网页加载过程。下面…

2652. 倍数求和

2652. 倍数求和 题目方法-【枚举】 & 题目特征-【求计算在给定范围内满足某种条件的整数之和】方法-【容斥原理】 & 题目特征-【计算满足多个条件的元素之和&#xff0c;并且需要避免重复计数】 题目 题目链接&#xff1a;https://leetcode.cn/problems/sum-multiples…

Idea集成Docker

1、前言 上一节中&#xff0c;我们介绍了Dockerfile的方式构建自己的镜像。但是在实际开发过程中&#xff0c;一般都会和开发工具直接集成&#xff0c;如Idea。今天就介绍下idea和Docker如何集成。 2、开启docker远程 要集成之前&#xff0c;需要我们本机能够访问docker服务…

每日一题 2652. 倍数求和(简单)

最简单的做法&#xff0c;遍历求和&#xff0c;时间O(n) class Solution:def sumOfMultiples(self, n: int) -> int:return sum([i if (i % 3 0) or (i % 5 0) or (i % 7 0) else 0 for i in range(n 1)])如果只求在 [1,n] 内能被m整除的数之和&#xff0c;那么 ans (…

[MQ]Win平台RocketMQ安装启动

1、下载 官网下载地址&#xff1a;https://rocketmq.apache.org/zh/download 2、解压ZIP包 解压rocketmq-all-x.x.x-bin-release.zip到目录。 比如我解压到了E:\Env\MQ_rocket\rocketmq-all-5.1.4-bin-release 3、配置环境变量 ROCKETMQ_HOME 4、RocketMQ JVM内存配置 这个需要…

3dmax中的 (Corona 9)cr渲染器怎么渲染?cr渲染器使用教程

Corona 9渲染器在3ds Max和Cinema 4D中应用广泛&#xff0c;是一款高效且功能强大的渲染器&#xff0c;得到了许多用户的好评。 Corona 9有以下几个主要的特点&#xff1a; 出色的渲染速度&#xff1a;Corona 9被证明是一个快速且高效的渲染引擎&#xff0c;它能够在保证高质…

虹科 | 解决方案 | 虹科Pico振动异响(NVH)诊断方案

车辆行驶过程中的偶发性异响&#xff08;比如经过颠簸路面时的吱嘎声&#xff09;和某一特定车速/转速下持续/周期性出现的异响&#xff0c;要将故障重现并定位故障点&#xff0c;对维持技师来讲是个重大的挑战。传统的测试方法是使用底盘听诊器&#xff0c;车辆一边在路上跑&a…

一文带你认识高速低侧栅极驱动器 FAN3111ESX 带你深入了解其特点及应用

FAN3111ESX一款低端驱动器产品&#xff0c;是外部 DC 2 至 5 V 参考输入、单通道同相输出、1.4 A 峰值灌电流、1.4 A 源电流低端栅极驱动器。 FAN3111ESX 1A栅极驱动器为驱动一个在低侧开关应用中的 N沟道增强型 MOSFET 而设计。 对于使用低压控制器和其它和驱动器相比使用更…

云计算是什么?学习云计算能做什么工作?

很多人经常会问云计算是什么&#xff1f;云计算能干什么&#xff1f;学习云计算能做什么工作&#xff1f;其实我们有很多人并不知道云计算是什么&#xff0c;小知今天来给大家讲讲学习云计算能做什么。 中国的云计算行业目前正处于快速发展阶段&#xff0c;随着互联网和数字化…

JMeter连接数据库

一. 下载数据库驱动jar包 https://jdbc.postgresql.org/download/ 二. 将数据库驱动放到jmeter的lib目录下 三. 在jmeter中引用这个jar包 四. 添加一个jdbc数据库连接配置 五. 添加一个jdbc request来查询sql并将查询结果赋值给一个变量 六. 将查询结果用于其他请求

防雷接地的作用和施工案例方案

防雷接地是一种防止雷电对建筑物、设备和人员造成危害的措施&#xff0c;它通过将建筑物或设备的金属部件与大地电位相连&#xff0c;使雷电流能够安全地泄放到地下&#xff0c;从而避免电击、火灾、爆炸等事故的发生。 地凯科技防雷接地系统一般由三个部分组成&#xff1a;接…

基于Java的教学评价管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…

一个标准的异常枚举类可以如何定义

一个标准的异常枚举类可以如何定义&#xff1f; 首先&#xff0c;使用enum关键字定义一个枚举类型。在枚举类型中&#xff0c;定义各种异常类型&#xff0c;每个异常类型用一个枚举常量表示。为每个异常类型添加一个构造方法&#xff0c;用于初始化异常类型的信息。可以为异常…

python 之enumerate()函数

文章目录 enumerate() 是 Python 中的一个内置函数&#xff0c;它用于在遍历可迭代对象&#xff08;如列表、元组、字符串等&#xff09;时同时获取每个元素的索引和值。这个函数非常有用&#xff0c;因为它允许您在迭代过程中轻松地访问元素的索引&#xff0c;而不需要手动维护…

linux centos7提示 cannot found font installed on the system.calibri

主图 目录 1.问题描述2.问题解决2.1安装Microsoft Core Fonts2.2手动安装字体文件&#xff1a;2.3查看当前系统基本型细腻 总结参考 文章所属专区 超链接 1.问题描述 linux centos7提示 cannot found font installed on the system.calibri &#xff0c;linux系统找不到cali…

Adobe Premiere Pro 和 After Effects 安装出错的解决路径

在有点年头的电脑上安装Premiere Pro 和 After Effects 遇到了前所未有的的麻烦&#xff0c;请了某宝上的小哥进行远程安装&#xff0c;两个软件倒是可以用了&#xff0c;但Win11系统无法正常关机&#xff0c;用了几天系统除了关机时会蓝屏几十秒&#xff0c;其他没有发现毛病&…

node多版本管理器nvm

node多版本管理器nvm 1、为何要使用node版本管理器2、nvm安装步骤2-1、卸载系统中的node2-2、下载nvm2-3、安装 3、维护node版本3-1、安装指定版本node3-2、查看本机已安装的所有node版本3-3、切换本机node版本 1、为何要使用node版本管理器 在日常开发中&#xff0c;难免会遇…

云服务器ip使用细节(公网、私有)

场景&#xff1a; 当我们对tcp服务器进行监听的时候&#xff0c;可能需要用到ip地址&#xff0c;比如使用httplib::Service::listen(ip, port)&#xff0c;而当我们访问tcp服务器时也需要ip地址 但这两个ip是不同的&#xff01; 每个云服务器通常都会有一个公网IP地址和一个私有…