4.2 C++ Boost 内存池管理库

Boost 库是一个由C/C++语言的开发者创建并更新维护的开源类库,其提供了许多功能强大的程序库和工具,用于开发高质量、可移植、高效的C应用程序。Boost库可以作为标准C库的后备,通常被称为准标准库,是C标准化进程的重要开发引擎之一。使用Boost库可以加速C应用程序的开发过程,提高代码质量和性能,并且可以适用于多种不同的系统平台和编译器。Boost库已被广泛应用于许多不同领域的C++应用程序开发中,如网络应用程序、图像处理、数值计算、多线程应用程序和文件系统处理等。

C++的指针操作可以说是继承了C语言的优点,但同时也带来了一些问题,例如内存泄漏、悬挂指针、访问越界等。这些问题不仅会导致程序运行错误,还会对系统稳定性造成影响。为了避免这些问题,Boost库提供了一套高效的自动内存管理指针操作函数,这些函数使用引用计数技术来管理内存。

2.1 使用Pool内存池

boost::pool是Boost库中一个内存池管理器,用于高效地管理和分配内存。在程序中,动态分配和释放内存是很常见的操作,但频繁的内存分配和释放会导致开销很大,影响程序性能。boost::pool针对这个问题提供了一个解决方案,它可以预分配并缓存一定数量的内存块,通过重复利用这些内存块来减小内存分配释放的开销,提高程序性能。

#include <iostream>
#include <boost/pool/pool.hpp>using namespace std;
using namespace boost;int main(int argc, char const *argv[])
{boost::pool<> pool(sizeof(int));                     // 定义整数内存池(int/float/double)int *ptr[10] = { 0 };                                // 定义指针列表for (int x = 0; x < 10; x++){ptr[x] = static_cast<int *>(pool.malloc());     // 开辟空间并转为指针if (ptr[x] == nullptr)cout << "分配空间失败" << endl;}// 分别对内存空间赋值for (int x = 0; x < 10; x++)*ptr[x] = x;// 输出数据for (int x = 0; x < 10; x++){cout << "内存地址: " << &ptr[x] << " 数值: " << *ptr[x] << endl;}getchar();return 0;
}

Pool内存池同样提供了对容器的存储方法,我们在使用时只需要包含头文件pool_alloc.hpp,当包含此头文件后读者可使用pool_allocator模板类对容器内的特殊成员进行初始化。

#include <iostream>
#include <string>
#include <vector>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>using namespace std;
using namespace boost;typedef struct
{int uuid;string uname;
}MyStruct;int main(int argc, char const *argv[])
{// 应用标准容器: 定义存储string类型的容器std::vector<std::string, pool_allocator<std::string> > vect;// 设置容器vect.push_back("admin");vect.push_back("lyshark");for (int x = 0; x < vect.size(); x++){std::cout << "输出: " << vect[x] << std::endl;}// 应用自定义数据类型std::vector<MyStruct, pool_allocator<MyStruct>> pool_ptr;MyStruct ptr;ptr.uuid = 10001;ptr.uname = "lyshark";pool_ptr.push_back(ptr);ptr.uuid = 1002;ptr.uname = "admin";pool_ptr.push_back(ptr);for (int x = 0; x < pool_ptr.size(); x++){std::cout << "UUID: " << pool_ptr[x].uuid << " Name: " << pool_ptr[x].uname << std::endl;}std::system("pause");return 0;
}

2.2 使用ObjectPool内存池

boost::object_pool是Boost库中的一个内存池管理器,可以用来高效地分配和释放内存,并能够管理多个大小相等的对象。

在使用boost::object_pool时,我们可以先创建一个大小固定的内存池,然后使用malloc()函数从内存池中分配内存,并在内存上构造一个对象。当我们需要释放内存时,可以调用destroy()函数显式地销毁对象,并使用free()函数释放内存。

#include <iostream>
#include <string>
#include <boost/pool/object_pool.hpp>using namespace std;
using namespace boost;struct MyStruct
{
public:int uuid;string uname;int uage;MyStruct(int uuid_, string uname_, int uage_){uuid = uuid_; uname = uname_; uage = uage_;}
};int main(int argc, char const *argv[])
{boost::object_pool<MyStruct> object;auto ptr = object.malloc();// 默认最多只能传递3个参数ptr = object.construct(1001,"lyshark",25);       // 为构造函数传递参数cout << "姓名: " << ptr->uname << endl;std::system("pause");return 0;
}

一般在默认情况下object_pool内存池只能接收三个以内的参数传递,当读者需要使用多于三个参数时则需要使用自定义可变参数模板来实现功能,我们以接受四个参数为例,定义construct模板并在该模板内部实现分配资源。

#include <iostream>
#include <string>
#include <boost/pool/object_pool.hpp>using namespace std;
using namespace boost;struct MyStruct
{
public:int uuid;string uname;int uage;string usex;MyStruct(int uuid_, string uname_, int uage_, string usex_){uuid = uuid_; uname = uname_; uage = uage_; usex = usex_;}
};// 定义可变参数模板,用来实现接受三个以上的参数
template<typename P, typename ... Args> inline typename P::element_type* construct(P& p, Args&& ... args)
{typename P::element_type* mem = p.malloc();new(mem) typename P::element_type(std::forward<Args>(args)...);return mem;
}int main(int argc, char const *argv[])
{boost::object_pool<MyStruct> object;auto ptr = object.malloc();// 接收四个参数写法auto ref = construct(object, 1001, "lyshark", 24, "男");cout << "姓名: " << ref->uname << endl;object.free(ref);object.free(ptr);std::system("pause");return 0;
}

2.3 使用SharedPtr智能指针

boost::shared_ptr是Boost库中的一个智能指针,用于自动管理动态分配的内存。它跟踪有多少个shared_ptr实例共享同一个对象,当最后一个实例离开作用域时,它会自动释放分配的内存。

该函数是boost.smart_ptr库中最重要的智能指针,shared_ptr包装了new操作符在堆上分配的动态对象,实现了引用计数型的智能指针,可被自由的拷贝和赋值,并在任意地方共享。

#include <iostream>
#include <string>
#include <boost/smart_ptr.hpp>using namespace std;
using namespace boost;int main(int argc, char const *argv[])
{// 基本的定义与赋值boost::shared_ptr<int> int_ptr(new int);*int_ptr = 1024;cout << "指针: " << &int_ptr << " 数值: " << *int_ptr << endl;boost::shared_ptr<string> string_ptr(new string);*string_ptr = "hello lyshark";cout << "指针: " << &string_ptr << " 长度: " << string_ptr->size() << endl;// 拷贝构造的使用boost::shared_ptr<int> shared_ptr(new int(10)); // 定义指向整数的sharedcout << "持有者: " << shared_ptr.unique() << endl;boost::shared_ptr<int>shared_copy = shared_ptr;   // 实现拷贝cout << "引用数: " << shared_ptr.use_count() << endl;shared_ptr.reset();   // 关闭shared的使用getchar();return 0;
}

在有时候我们需要使用多个指针,并将多个指针分别指向不同的数据集合,此时我们可以先封装一个MyShared类,并使用循环的方式初始化创建内存空间,每次创建空间后将该空间存储至vect容器内,最后再以此循环输出该容器内存所有自定义类元素即可;

#include <iostream>
#include <vector>
#include <string>
#include <boost/shared_ptr.hpp>using namespace std;
using namespace boost;// 定义Shared类
class MyShared
{
private:int shared_uuid;std::string shared_name;public:MyShared(int x, std::string y){shared_uuid = x;shared_name = y;}std::string GetName(){return shared_name;}int GetUUID(){return shared_uuid;}
};int main(int argc, char const *argv[])
{std::vector<boost::shared_ptr<MyShared>> vect;// 循环开辟空间,并放入vector列表for (int x = 0; x < 5; x++){boost::shared_ptr<MyShared> ptr(new MyShared(x,"hello lyshark"));vect.push_back(ptr);}// 输出列表中的元素for (int x = 0; x < vect.size(); x++){std::cout << "UUID: " << vect[x]->GetUUID() << " Name: " << vect[x]->GetName() << std::endl;}std::system("pause");return 0;
}

智能指针同样支持使用引用计数器功能,在指针内部读者可通过使用ptr.use_count()来输出当前的计数器,当此处代码没有被使用是则引用计数器会为0,而当代码或多个进程使用时则引用计数器相应的会增加,查询引用计数器可以如下所示;

#include <iostream>
#include <string>
#include <boost/shared_ptr.hpp>using namespace std;
using namespace boost;class MyShared
{
private:boost::shared_ptr<int> ptr;
public:MyShared(boost::shared_ptr<int> p_) :ptr(p_){}void print(){cout << "内部 计数: " << ptr.use_count() << " 数值: " << *ptr << endl;}
};// 自动拷贝一个对象,所以引用计数会+1
void print_func(boost::shared_ptr<int> ptr)
{cout << "外部 计数: " << ptr.use_count() << " 数值: " << *ptr << endl;
}int main(int argc, char const *argv[])
{boost::shared_ptr<int> ptr(new int(100));     // 定义整数指针MyShared s1(ptr), s2(ptr);                    // 定义两个对象,并初始化s1.print();s2.print();*ptr = 200;print_func(ptr);s1.print();s2.print();std::system("pause");return 0;
}

如上,在声明了shared_ptr和两个MyShared类后,指针被共享,因此引用计数为3,调用print_func()函数,该函数内部拷贝了一个shared_ptr对象,因此引用计数再次增加1,但退出函数时,拷贝自动析构,引用计数又会恢复为3。

2.4 使用MakeShared工厂函数

boost::make_shared是一个工厂函数,用于动态分配一个对象并返回一个智能指针,它是Boost库中的一个组件。使用make_shared我们可以将对象的构造和内存分配合并在一起,避免了常规构造函数和动态内存分配的性能损失和代码冗余。

当读者使用2.3节中所示的shared_ptr智能指针时,虽然能够很好的消除delete释放的调用,但我们还是需要使用new方法来构造初始化数据集,为了能够不再使用new关键字,在smart_ptr库中提供了一个工厂函数make_shared()函数,用于消除使用new创建数据集,工厂函数常用于初始化特定的指针数据,如下所示;

#include <iostream>
#include <string>
#include <vector>
#include <boost/smart_ptr.hpp>using namespace std;
using namespace boost;int main(int argc, char const *argv[])
{// make_shared 工厂函数初始化boost::shared_ptr<string> string_ptr = boost::make_shared<string>("hello lyshark");cout << "初始化字符串: " << *string_ptr << endl;// 应用于标准容器中typedef std::vector<boost::shared_ptr<int>> vector_ptr;   // 定义标准容器类型vector_ptr vect(10);                                      // 定义拥有十个元素的容器// 初始化赋值int x = 0;for (auto pos = vect.begin(); pos != vect.end(); ++pos){(*pos) = boost::make_shared<int>(++x);        // 工厂函数初始化cout << "输出值: " << *(*pos) << endl;        // 输出数据(两次解引用)}// 修改数据boost::shared_ptr<int> int_ptr = vect[9]; // 获取最后一个数值*int_ptr = 100;                           // 修改最后一个数值cout << "修改后: " << *vect[9] << endl;// 第二种输出方式(一次解引用完成)x = 0;for (auto& ptr : vect){cout << "输出值: " << *ptr << endl;}std::system("pause");return 0;
}

2.5 使用SharedPtr桥接模式

在C++中,shared_ptr有一种常用的设计模式是桥接模式(Bridge Design Pattern)又称为PIMPL模式。桥接模式的主要作用是将实现细节从类的接口中分离出来,从而使得接口和实现可以独立变化,提高了类的可扩展性和可维护性。

使用shared_ptr实现桥接模式时,我们可以使用一个基类和多个派生类的继承关系,并使用shared_ptr来管理对象的生命周期。通过使用shared_ptr的引用计数技术,可以动态地改变派生类的具体实现,而不会影响到基类接口的实现。其仅对外部暴漏最小的细节,内部类实现用一个shared_ptr来保存指针。

如下代码所示,首先我们定义MyShared作为基类,其内部存在一个print输出函数,而该函数通过boost::shared_ptr<impl> ptr;指向impl基址,当输出内容时,自动桥接到impl派生类上的print函数上。

#include <iostream>
#include <string>
#include <vector>
#include <boost/smart_ptr.hpp>using namespace std;
using namespace boost;// 定义基类
class MyShared
{
private:class impl;boost::shared_ptr<impl> ptr;public:MyShared();void print();
};// 定义桥接类
class MyShared::impl
{
public:void print(){cout << "输出桥接类" << endl;}
};MyShared::MyShared() :ptr(new impl){}void MyShared::print()
{ptr->print();
}int main(int argc, char const *argv[])
{MyShared lsp;lsp.print();std::system("pause");return 0;
}

2.6 使用SharedPtr工厂模式

在C++中,shared_ptr还可以与工厂模式(Factory Design Pattern)结合使用,工厂模式是一种创建型设计模式,该模式包装了new操作符的使用,使对象的创建工作集中在工厂类或工厂函数上,通过创建和返回智能指针,从而实现动态创建对象并自动管理其生命周期的功能。

通常开发中,自己编写的工厂类都会在堆上使用new动态分配对象,然后返回对象指针,当忘记释放delete时,内存泄漏就会产生。当使用shared_ptr实现工厂模式时,我们可以将工厂类中的创建对象的方法返回一个shared_ptr对象,从而避免手动管理动态分配的内存。

如下代码所示,我们使用shared_ptr封装接口,让impl类不再返回原始指针,而是返回shared_ptr包装的智能指针,这样就可以很好的保护资源。

#include <iostream>
#include <boost/smart_ptr.hpp>using namespace std;
using namespace boost;// 定义基类 全部为虚函数
class abstract
{
public:virtual void MyPrint() = 0;
protected:virtual ~abstract() = default;
};// 派生类实现虚函数
class impl :public abstract
{
public:impl() = default;virtual ~impl() = default;public:virtual void MyPrint(){cout << "调用方法完成." << endl;}
};// 工厂函数返回基类的 基址指针 返回类型为 shared_ptr
boost::shared_ptr<abstract> create()
{return boost::make_shared<impl>();
}int main(int argc, char const *argv[])
{// 第一种调用方式auto ptr = create();ptr->MyPrint();// 第二种方式abstract *abstract_ptr = ptr.get();abstract_ptr->MyPrint();// 强制转换,后输出impl *impl_ptr = (impl*)(ptr.get());impl_ptr->MyPrint();std::system("pause");return 0;
}

2.7 使用SharedPtr资源共享

使用shared_ptr实现资源共享时,我们可以创建多个shared_ptr对象,让它们共同管理同一个动态分配的对象,从而避免了内存泄漏和错误释放内存的情况。

如下案例中我们定义了shared_vector类,当MyShared中的内容发生变化时,由于ptr指向了MyShared类,则ptr中的值也会随着MyShared中的内容的变化而变化。

#include <iostream>
#include <memory>
#include <vector>
#include <string>using namespace std;class shared_vector
{
public:typedef vector<string>::size_type size_type;shared_vector() : data(make_shared<vector<string>>()){}shared_vector(initializer_list<string> il) : data(make_shared<vector<string>>(il)){}size_type size()const{ return data->size(); }bool empty()const{ return data->empty(); }//尾部插入删除元素                                                          void push_back(const string& s){ data->push_back(s); }void pop_back(){ data->pop_back(); }//访问元素                                                                    string& front(){ return data->front(); }string& back(){ return data->back(); }
private:shared_ptr<vector<string>> data;
};int main(int argc, char const *argv[])
{shared_vector MyShared{ "admin", "lyshark" };shared_vector ptr(MyShared);ptr.push_back("administrator");cout << "发生变化: " << MyShared.back() << endl;std::system("pause");
}

2.8 使用WeakPtr智能指针

weak_ptr是C++11中的智能指针,它用于解决shared_ptr可能引起的循环引用问题。与shared_ptr不同,weak_ptr并不持有所指对象的所有权,因此它不能直接访问所指向的对象。它只是提供了一种通过shared_ptr访问所指向对象的方式,并且在没有引用时可以自动弱化其引用。

在使用weak_ptr时,通常需要先从一个shared_ptr对象创建一个weak_ptr对象。我们可以通过lock()函数获取指向所指对象的shared_ptr对象,然后通过这个shared_ptr对象来访问所指对象。

如果简单来说,这个指针的出现只是为了配合shared_ptr使用的,其本身并不具备普通指针的行为,其主要的作用在于协助shared_ptr工作,像旁观者一样观察资源的使用情况。

#include <iostream>
#include <boost/smart_ptr.hpp>using namespace std;
using namespace boost;int main(int argc, char const *argv[])
{boost::shared_ptr<int> ptr(new int(10));boost::weak_ptr<int> weak(ptr);// 判断weak_ptr观察的对象是否失效if (!weak.expired()){// 获得一个shared_ptrboost::shared_ptr<int> new_ptr = weak.lock();*new_ptr = 100;}ptr.reset();std::system("pause");return 0;
}

weak_ptr还可以用于对象自我管理,如获得this指针的shared_ptr使对象自己能产生shared_ptr管理自己,使用时需要定义类,并继承于enable_shared_from_this接口。

#include <iostream>
#include <boost/smart_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>using namespace std;
using namespace boost;class self_my_shared_ptr : public boost::enable_shared_from_this<self_my_shared_ptr>
{
public:self_my_shared_ptr(int n) :x(n){}int x;void print(){std::cout << "自身指针: " << x << std::endl;}
};int main(int argc, char const *argv[])
{auto ptr = boost::make_shared<self_my_shared_ptr>(100);ptr->print();auto p = ptr->shared_from_this();p->x = 200;p->print();std::system("pause");return 0;
}

有时候代码中可能会出现循环引用的情况,此时使用shared_ptr指针时计数器就会失效,导致无法正确释放资源,例如如下一个案例,两个节点对象互相持有对方的引用,每个引用计数器都是2,在析构时引用计数没有变为0,因此不会调用删除清理操作,所以会导致内存泄漏的产生。

#include <iostream>
#include <boost/smart_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>using namespace std;
using namespace boost;class node
{
public:~node(){std::cout << "析构函数,被调用." << std::endl;}typedef boost::shared_ptr<node> ptr_type;ptr_type next;
};int main(int argc, char const *argv[])
{auto ptrA = boost::make_shared<node>();auto ptrB = boost::make_shared<node>();ptrA->next = ptrB;ptrB->next = ptrA;std::cout << "ptrA 计数器: "<< ptrA.use_count() << std::endl;std::cout << "ptrB 计数器: " << ptrB.use_count() << std::endl;std::system("pause");return 0;
}

为了解决上述的内存泄露问题,我们需要使用weak_ptr智能指针,将原来的强引用模式改为弱引用模式,即可实现动态释放,循环引用即可消失。

#include <iostream>
#include <boost/smart_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>using namespace std;
using namespace boost;class node
{
public:~node(){std::cout << "析构函数,被调用." << std::endl;}typedef boost::weak_ptr<node> ptr_type;ptr_type next;
};int main(int argc, char const *argv[])
{auto ptrA = boost::make_shared<node>();auto ptrB = boost::make_shared<node>();ptrA->next = ptrB;ptrB->next = ptrA;std::cout << "ptrA 计数器: "<< ptrA.use_count() << std::endl;std::cout << "ptrB 计数器: " << ptrB.use_count() << std::endl;// 检查弱引用是否有效if (!ptrA->next.expired()){// 获取到强引用指针auto ptrC = ptrA->next.lock();}std::system("pause");return 0;
}

2.9 使用IntrusivePtr计数器

intrusive_ptr是一个智能指针,与shared_ptr类似,都具有引用计数的功能。它是一个轻量级的智能指针,相比于标准库中的shared_ptr,intrusive_ptr可以方便地在自定义数据结构中使用,因为它不需要在自定义类型中维护额外的引用计数器。

该指针采用了惯用法,即将引用计数器作为自定义类型的一部分存储在实例中。因此,使用intrusive_ptr时,需要为自定义类型提供一个内部引用计数器的实现。

#include <iostream>
#include <boost/smart_ptr.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>using namespace std;
using namespace boost;struct data
{int m_count = 0;~data(){cout << "结束." << endl;}
};// 递增
void intrusive_ptr_add_ref(data* p)
{p->m_count = p->m_count + 10;
}// 递减
void intrusive_ptr_release(data* p)
{if (--p->m_count == 0){delete p;}
}int main(int argc,char *argv[])
{// 使用自定义引用计数typedef intrusive_ptr<data> counted_ptr;counted_ptr p(new data);std::cout << "引用数: " << p->m_count << std::endl;counted_ptr weak_p(p.get(), false);std::cout << "引用数: " << p->m_count << std::endl;std::system("pause");return 0;
}

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/bc8ff67e.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

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

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

相关文章

cmake扩展(5)——file命令排除部分文件

在cmake中可以使用file命令获取需要的文件&#xff0c;并且支持正则/通配符&#xff0c;使用起来还是很方便的。 #语法file({GLOB | GLOB_RECURSE} <out-var> [...] [<globbing-expr>...])#example file(GLOB_RECURSE SOURCES "src/*.h" "src/*.cp…

HTTP与HTTPS的区别

面试常见问题&#xff0c;HTTPS优化总结易记版&#xff1a; 1、HSTS重定向技术&#xff1a;将http自动转换为https&#xff0c;减少301重定向 2、TLS握手优化&#xff1a;在TLS握手完成前客户端就提前向服务器发送数据 3、会话标识符&#xff1a;服务器记录下与某客户端的会…

Mac鼠标增强工具Smooze Pro

Smooze Pro是一款Mac上的鼠标手势增强工具&#xff0c;可以让用户使用鼠标手势来控制应用程序和系统功能。 它支持多种手势操作&#xff0c;包括单指、双指、三指和四指手势&#xff0c;并且可以自定义每种手势的功能。例如&#xff0c;您可以使用单指向下滑动手势来启动Expos视…

Linux 僵死进程

fork复制进程之后&#xff0c;会产生一个进程叫做子进程&#xff0c;被复制的进程就是父进程。不管父进程先结束&#xff0c;还是子进程先结束&#xff0c;对另外一个进程完全没有影响&#xff0c;父进程和子进程是两个不同的进程。 一、孤儿进程 现在有以下代码&#xff1a;…

如何计算全彩LED显示屏的像素

大屏尺寸 提供大屏的尺寸和像素点间距&#xff0c;计算大屏的分辨率是多少&#xff1f; 大屏尺寸&#xff1a;宽度>10200mm&#xff0c;高度>2025mm&#xff1b;像素点间距<1.25mm 分辨率计算 宽10200/1.258160px 高2025/1.251620px 宽&#xff1a;高 接近 5:1&a…

PHP 三元 !empty 而不是评估为真或假 可用isset()

是否可以使用速记三元来检查变量是否已设置&#xff0c;而不是是否计算结果为零或非零&#xff1f; 例如&#xff0c;我试过&#xff1a; $var 0; echo (string) $var ?: (string) false ?: 2;但由于前两个表达式的计算结果均为“0”或“false”&#xff0c;因此显示为 2。…

如何建立单元测试

快速开始 zixun-quickstart-mk3生成的项目已经配置好了基础的BaseTest,各个测试类只需要继承BaseTest就可以开始进行单元测试的编写了。 如何进行Mock 为了保证独立性和可重复执行,所有的外部依赖都需要进行Mock,SpringTest引入了Mockito作为单测Mock组件, Mickito官方文…

vue2中使用unocss,在ie11 不能正常显示unocss的颜色设置

前景 ie11 不能正常显示unocss的颜色设置&#xff1a;“–un-text-opacity: 1 !important; color: rgba(213,147,197,var(–un-text-opacity));” IE11 目前是无法支持 CSS 变量&#xff08;自定义属性&#xff09;和 rgba 中使用的 var() 函数。 为了在 IE11 中使用 UnoCSS…

前端基础(JavaScript语法)

前言&#xff1a;今天学习JavaScript的基本语法&#xff0c;包括变量、函数、对象、数组。 目录 JavaScript 变量 函数 对象 数组 JavaScript 变量 定义变量 判断语句 判断等于&#xff1a; 判断不等于&#xff1a;! if else语句 if(vavb){ console.log("…

springboot 整合swagger 入门 使用

1.前提 一定要看好版本。 Springboot ✚ Swagger各版本整理_swagger版本_qq_33334411的博客-CSDN博客 我的版本: <dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</ver…

前馈神经网络正则化例子

直接看代码&#xff1a; import torch import numpy as np import random from IPython import display from matplotlib import pyplot as plt import torchvision import torchvision.transforms as transforms mnist_train torchvision.datasets.MNIST(root…

Docker:Windows container和Linux container

点击"Switch to Windows containers"菜单时&#xff1a; 提示 然后 实际上是运行&#xff1a;com.docker.admin.exe start-service

成集云 | 乐享问题邀请同步企微提醒 | 解决方案

源系统成集云目标系统 方案介绍 腾讯乐享是腾讯公司开发的一款企业社区化知识管理平台&#xff0c;它提供了包括知识库、问答、课堂、考试、活动、投票和论坛等核心应用。这个平台凝聚了腾讯10年的管理经验&#xff0c;可以满足政府、企业和学校在知识管理、学习培训、文化建…

【gitkraken】gitkraken自动更新问题

GitKraken 会自动升级&#xff01;一旦自动升级&#xff0c;你的 GitKraken 自然就不再是最后一个免费版 6.5.1 了。 在安装 GitKraken 之后&#xff0c;在你的安装目录&#xff08;C:\Users\<用户名>\AppData\Local\gitkraken&#xff09;下会有一个名为 Update.exe 的…

Linux环境变量

环境变量 一.基本概念二.常见的环境变量1.PATH&#xff1a;指令搜索路径2.HOME&#xff1a; 指定用户的主工作目录3.SHELL&#xff1a;当前Shell,它的值通常是/bin/bash 三.查看环境变量的方法四.命令行参数五.环境变量增加和删除六.本地变量 一个问题&#xff1a;我们在写一段…

Kotlin~Bridge桥接模式

概念 抽象和现实之间搭建桥梁&#xff0c;分离实现和抽象。 抽象&#xff08;What&#xff09;实现&#xff08;How&#xff09;用户可见系统正常工作的底层代码产品付款方式定义数据类型的类。处理数据存储和检索的类 角色介绍 Abstraction&#xff1a;抽象 定义抽象接口&…

《Go 语言第一课》课程学习笔记(五)

入口函数与包初始化&#xff1a;搞清 Go 程序的执行次序 main.main 函数&#xff1a;Go 应用的入口函数 Go 语言中有一个特殊的函数&#xff1a;main 包中的 main 函数&#xff0c;也就是 main.main&#xff0c;它是所有 Go 可执行程序的用户层执行逻辑的入口函数。 Go 程序在…

一起创建Vue脚手架吧

目录 一、安装Vue CLI1.1 配置 npm 淘宝镜像1.2 全局安装1.3 验证是否成功 二、创建vue_test项目2.1 cmd进入桌面2.2 创建项目2.3 运行项目2.4 查看效果 三、脚手架结构分析3.1 文件目录结构分析3.2 vscode终端打开项目 一、安装Vue CLI CLI&#xff1a;command-line interface…

日常BUG——微信小程序提交代码报错

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;日常BUG、BUG、问题分析☀️每日 一言 &#xff1a;存在错误说明你在进步&#xff01; 一、问题描述 在使用微信小程序开发工具进行提交代码时&#xff0c;报出如下错误&#xff1a; Invalid a…

Git提交规范指南

在开发过程中&#xff0c;Git每次提交代码&#xff0c;都需要写Commit message&#xff08;提交说明&#xff09;&#xff0c;规范的Commit message有很多好处&#xff1a; 方便快速浏览查找&#xff0c;回溯之前的工作内容可以直接从commit 生成Change log(发布时用于说明版本…