当前位置: 首页 > news >正文

c++单例实践

C++单例实践

在日常开发中,虽然太多的单例调用会让代码的耦合度变高,但是例如日志类这种,单例模式就变得非常有。所以这篇文章为大家介绍static 关键字相关知识以及如何实现自己的C++单例类。

static关键字

首先让我们请出今天的主角: static。C++中有一个关键字——static,用static修饰的变量或者函数,都会变得不同。根据cpp reference中关于static的描述,static主要有几个作用:

  1. 使全局变量变为内部链接性。
  2. 修饰块作用域变量,其静态存储时间跟随程序并且只会初始化一次。
  3. 修饰类成员,使其与类相关,而非对象。

修饰全局变量

首先补充一下全局变量相关的基础:
假设你在头文件中定义了一个变量,此时你在多个文件里都包含了这个头文件,因为你想在这些地方都共用这个变量。然后你编译代码,发现编译器报错并提示你“xxx重定义”。

// 1.h
int GlobalVar = 1;// 1.cpp
#include "1.h"
int LocalVar = GlobalVar;// main.cpp
#include "1.h"int main()
{int var = GlobalVar;return 0;
}

问题出在哪呢?让我们回顾一下预处理相关的知识,包含一个头文件,编译器在预处理阶段就会将头文件中的内容展开。回到刚刚的问题,你在多个文件中都包含这个头文件,此时编译器发现有多个地方都声明并且定义了一个GlobalVar,所以就会报错。那么怎样能够在别的文件中使用这个变量呢?别担心,C++提供了extern关键字,来帮助你使用全局变量:

// 1.h
extern int GlobalVar;// 1.cpp
#include "1.h"
int GlobalVar = 1;
int LocalVar = GlobalVar;// main.cpp
#include "1.h"int main()
{int var = GlobalVar;return 0;
}

你需要在1.h中使用extern声明这个变量,然后在1.cpp中定义这个全局变量。此时任何包含1.h的地方都能够正常使用这个全局变量了。
回到正轨,用static修饰这个全局变量,会有什么效果呢?用static修饰变量,那么这个变量将会变成内部链接性,什么叫内部链接性呢?就是说这个变量只在当前源文件才被可见,即使用extern修饰也不行

// 1.h
static int StaticGlobalVar = 2;
void funct();// 1.cpp
#include "1.h"
int GlobalVar = StaticGlobalVar;
void funct()
{std::cout << "address of: " << std::addressof(StaticGlobalValue) << std::endl;
}// main.cpp
#include "1.h"int main()
{funct();std::cout << "address of: " << std::addressof(StaticGlobalValue) << std::endl;return 0;
}

虽然这里在多个地方使用没有问题,但是打印变量的地址你就会发现,在不同的文件中使用,实际上就相当于是创建了两个变量。

address of: 00007FF6BABC16F8
address of: 00007FF6BABC16F0

所以,这也就是内部链接,也就是内部可见性,外部不可见

修饰块内局部变量

static修饰局部变量,变量的存储周期将会发生变化:从第一次定义这个变量起,到程序结束。

#include <iostream>void func()
{static int a = 0;int b = 0;a++;b++;std::cout << "a = " << a << "; b = " << b << std::endl;
}int main()
{func();func();return 0;
}

上面代码运行输出为:

a = 1; b = 1;
a = 2; b = 1;

从这我们可以得知,static修饰局部变量之后,其存储空间变成了Static Storage Duration,也就是随程序退出而结束。

修饰类成员

static修饰类成员,该成员变成类的静态成员,属于类,而非属于对象。当static修饰类的成员函数时,相比于成员变量会有一些限制:类的静态成员函数,只能访问类的静态成员,不能访问非静态成员。 这里不难理解,毕竟在没有实例化对象的时候,类的非静态成员还没有创建,此时通过静态函数访问非静态成员就会导致未定义行为。

#include <iostream>class MyClass
{
public:static void FuncStatic{std::cout << staticVar << std::endl;// error! 静态函数只能访问静态成员//std::cout << var << std::endl;}private:static int staticVar;int var;
};

创建属于自己的单例类

通过对上面的介绍,我们已经拥有了一把能够解决单例模式的利剑,让我们一步一步来创建一个属于自己的单例类。暂且将这个类命名为MyInstanceClass

饿汉式单例

实现一个单例,有以下几个点要求:

  1. 全局只有一个实例
  2. 提供了一个全局访问点来访问该实例

要实现全局只有一个实例,意味着不允许自己创建对象,聪明的你可以想到,将构造函数声明成private,这样外部就没办法调用构造函数,也就谈不上创建对象了。

class MyInstanceClass
{
private:MyInstanceClass();};

到这里,有的同学会问了:“构造函数私有化了,那还怎么创建唯一实例呢?” 还记得我们前面介绍过static变量吗?现在该到他出场的时候了。☝🤓我们可以定义一个static成员变量,众所周知,类的静态成员属于类,而不属于对象,这也就符合我们的要求:全局唯一实例。

class MyInstanceClass
{
private:MyInstanceClass();private:static MyInstanceClass instance;
};

PS: 顺带插一句:关于为什么静态成员变量能够调用私有的构造函数,网上说的是,静态成员变量是属于类的,并且这个静态成员变量是由编译器去进行初始化的,这个操作在main函数运行之前执行(别问,问就是编译器做的)。这一块可以看一本经典的书:《程序员的自我修养——链接、装载与库》中的11.4节:

对于每个编译单元(.cpp),GCC编译器会遍历其中所有的全局对象,生成一个特殊的函数,这个特殊函数的作用就是对本编译单元里的所有全局对象进行初始化。我们可以通过对本节开头的代码进行反汇编得到一些粗略的信息,可以看到GCC在目标代码中生成了一个名为_GLOBAL__I_Hw的函数,由这个函数负责本编译单元所有的全局\静态对象的构造和析构

现在,我们实现第二个点:提供一个全局访问点来访问。
我们通过定义一个public的静态成员函数GetInstance来获取这个全局实例。为什么要是静态成员函数呢?关于这个问题,首先要明确一个点,静态成员变量是属于类的。如果声明的不是static函数,那么需要实例化一个对象才能调用,而由于构造函数私有化又不能实例化对象,所以只能使用静态成员函数。此外static成员变量,需要在cpp文件里面进行定义。

// .h
class MyInstanceClass
{
public:static MyInstanceClass* GetInstance(){return &instance;}private:MyInstanceClass();private:static MyInstanceClass instance;
};//.cpp
// 定义
MyInstanceClass MyInstanceClass::instance;

同时为了防止能够通过拷贝构造函数来生成对象,我们显式的将拷贝构造函数和赋值运算符删除

// .h
class MyInstanceClass
{
public:static MyInstanceClass& GetInstance(){return instance;}private:MyInstanceClass();MyInstanceClass(const MyInstanceClass&) = delete;MyInstanceClass& operator=(const MyInstanceClass&) = delete;private:static MyInstanceClass instance;
};

通过调用MyInstanceClass::GetInstance()来获取这个实例。
看到这里,恭喜你🥳,你创建了一个饿汉型单例。什么叫“饿汉型单例”呢?顾名思义,饿汉饿汉,就是很饿了,马上就要吃东西,也就对应着这个单例实例在软件运行的时候就会创建

懒汉型单例

也许你是一个十分珍惜内存的开发者,这个单例在你不需要的时候,就占用了内存空间,这显然是不符合你的性格。优化!一定要优化🤬!聪明的你又想到了一个办法☝🤓,将成员变量改成指针,在需要用的时候再去创建不就好了。并且提供一个销毁函数,在程序退出的时候,调用析构函数,将占用的资源适当。像下面这样:

// .h
class MyInstanceClass
{
public:static MyInstanceClass& GetInstance(){if (instance == nullptr){instance = new MyInstanceClass;}return *instance;}void Destroy(){if (instance){delete instance;instance = nullptr;}}private:MyInstanceClass();MyInstanceClass(const MyInstanceClass&) = delete;MyInstanceClass& operator=(const MyInstanceClass&) = delete;private:static MyInstanceClass* instance;
};// .cpp
MyInstanceClass* MyInstanceClass::instance = nullptr;

为了防止忘记手动析构变量(不要相信自己一定会记得),你用上智能指针,自动管理内存。

// .h
#include <memory>class MyInstanceClass
{
public:static MyInstanceClass& GetInstance(){if (instance == nullptr){instance.reset(new MyInstanceClass);}return *instance;}private:MyInstanceClass();MyInstanceClass(const MyInstanceClass&) = delete;MyInstanceClass& operator=(const MyInstanceClass&) = delete;private:static std::unique_ptr<MyInstanceClass> instance;
};// .cpp
std::unique_ptr<MyInstanceClass> MyInstanceClass::instance;

恭喜你,创建了一个懒汉型单例(在第一次调用全局访问接口的时候,才初始化单例)。现在你拿着你的单例去应用到你的项目里面,发现十分的好用🤤,你尝试应用到更多的场景。正好你有一个应用多线程的项目,你也打蒜用你的单例,问题随之而来。

Double Check Lock Pattern(DCLP)

考虑一个多线程场景,两个线程A、B,在A线程第一次调用GetInstance,并判断instance == nullptr,满足条件准备构造的时候。线程B也调用了GetInstance,而此时线程A调用的instance = new MyInstanceClass;还没有返回,所以instance == nullptr仍然是满足的,此时又会调用一遍构造函数,此时就会导致内存泄漏(因为创建了两次,但是只保存了一个指针的地址)。image.png
为了解决这种多线程问题,你引入常见的处理多线程同步的机制——锁。在判断instance变量是否为nullptr时,加锁。

// .h 
#include <memory>
#include <mutex>class MyInstanceClass
{
public:static MyInstanceClass& GetInstance(){std::lock_guard<std::mutex> locker(mutex_);if (instance == nullptr){instance.reset(new MyInstanceClass);}return *instance;}private:MyInstanceClass();MyInstanceClass(const MyInstanceClass&) = delete;MyInstanceClass& operator=(const MyInstanceClass&) = delete;private:static std::unique_ptr<MyInstanceClass> instance;static std::mutex mutex_;
};// .cpp
std::unique_ptr<MyInstanceClass> MyInstanceClass::instance;
std::mutex MyInstanceClass::mutex_;

但是细心的又双叒想到了问题(盲生,你发现了华点🕵),其实真正的内存创建只会发生一次,但每一次调用不管内存创建有没有执行,都会执行加锁解锁的操作,这是不必要的浪费。你又会说:优化!一定要优化🤬!
于是你选择在加锁之前,再进行一次判空的操作,如果已经初始化完成,就直接返回instance。就不需要每一次都进行昂贵的加解锁操作。

// .h 
#include <memory>
#include <mutex>class MyInstanceClass
{
public:static MyInstanceClass& GetInstance(){if (instance == nullptr){std::lock_guard<std::mutex> locker(mutex_);if (instance == nullptr){instance.reset(new MyInstanceClass);}}return *instance;}private:MyInstanceClass();MyInstanceClass(const MyInstanceClass&) = delete;MyInstanceClass& operator=(const MyInstanceClass&) = delete;private:static std::unique_ptr<MyInstanceClass> instance;static std::mutex mutex_;
};// .cpp
std::unique_ptr<MyInstanceClass> MyInstanceClass::instance;
std::mutex MyInstanceClass::mutex_;

这种双重检查的操作,我们称之为:Double Check Lock Pattern(DCLP)。然而,DCLP也不像你想象中的那么稳妥,在多线程场景下,仍然是会有问题的。简单来说就是:
instance = new MyInstanceClass
这句代码分成三个步骤:

  1. 创内存
  2. 调构造
  3. 赋变量

但是编译器可能会把第二、第三两个步骤调换顺序,导致另外一个线程获取的变量是一个没有调用构造函数的变量。具体分析可以看文末参考中的第7条链接。
image.png
那么有没有一种方法能够让你放心大胆的在各种场景去使用这个单例呢?答案当然是有,而且还不止一种。

std::call_once

C++11新增了一个函数std::call_once,函数原型如下:

template< class Callable, class... Args >
void call_once( std::once_flag& flag, Callable&& f, Args&&... args );

这个函数保证你所传入的f只调用一次,那么把你的单例类稍作修改,就可以实现只构造一次的需求。

// .h 
class MyInstanceClass
{
public:static MyInstanceClass& GetInstance(){static std::once_flag s_flag;std::call_once(s_flag, [&]() {instance.reset(new Singleton);});return *instance;}private:MyInstanceClass();MyInstanceClass(const MyInstanceClass&) = delete;MyInstanceClass& operator=(const MyInstanceClass&) = delete;private:static std::unique_ptr<MyInstanceClass> instance;
};// .cpp
std::unique_ptr<MyInstanceClass> MyInstanceClass::instance;

Meyer’s Singleton

第二种方法采用在函数中创建一个静态局部变量的方式,利用函数内的静态变量只有在第一次调用,才会初始化的特性,你可以以一种非常简单的方式来实现单例模式。

// .h 
class MyInstanceClass
{
public:static MyInstanceClass& GetInstance(){static MyInstanceClass instance;return instance;}private:MyInstanceClass();MyInstanceClass(const MyInstanceClass&) = delete;MyInstanceClass& operator=(const MyInstanceClass&) = delete;
};

拓展:

Storage Duration & Linkage

此部分内容参考Storage class specifiers - cppreference.com,想要详细了解的同学请到这个链接去查看,本文只做简单介绍。

Storage Duration(存储周期)

存储周期,也就是变量什么时候被销毁,根据不同情况主要分为四种:

  1. 自动存储周期(Automatic Storage Duration)

    这种存储周期一般结束于当前的程序块。例如块作用域中的局部变量在结束当时块时就自动销毁。又比如函数中的形参,在函数结束后,就自动销毁。

  2. 静态存储周期(Static Storage Duration)

    此类存储周期跟随程序的退出而结束。例如全局变量或者static修饰的局部变量。

  3. 线程存储周期(Thread Storage Duration)

    此类存储周期跟随线程的退出而结束。注意,这个仅在C++11及之后版本才存在,因为C++11引入了thread_local修饰符。

  4. 动态存储周期(Dynamic Storage Duration)

    此类存储周期的存储周期取决于使用者。例如手动调用new和delete创建的对象。

Linkage(链接性)

同样,链接性也分成三种:

  1. 无链接性(No Linkage)

    此类链接性代表仅仅只能在同一作用域才能访问。例如函数内的局部变量(没有被explicit修饰),局部类和其成员函数等。

  2. 内部链接性(Internal Linkage)

    能够被当前翻译单元访问称之为内部链接性。例如用static修饰的变量、函数等;

  3. 外部链接性(External Linkage)

    能够被其他翻译单元访问的称之为外部链接性。例如有名命名空间下的类、枚举等,使用extern声明的变量等都具有外部链接性。

Static Members

在声明类成员(成员变量和成员函数)时,在前面加上一个static,即可将此成员定义成一个类的静态成员。静态成员拥有静态存储周期以及内部链接性。

基础

当static修饰类成员时,这个类成员就不再与类的对象(object)相关,而是与类相关。直白一点说就是不管你定义多少个对象,类的静态成员始终只有一个,它是与类相关的。

class MyStaticClass
{
public:static void StaticFunc(){}private:static int StaticVar;
};
Static Data Members

将类的成员变量用static修饰,它就成为了一个静态数据成员。需要注意的是,静态数据成员不能是mutable

如何定义以及初始化静态成员

根据给变量添加的不同的修饰符,同样也分几种情况:

  1. 普通静态成员

    这种成员不能在类里进行定义,需要在类外进行定义。但是从C++17开始,在static前面加上inline即可实现在类内定义。在类外的定义语法为:
    类型 类名::变量名;

    // .h
    class MyStaticClass
    {
    public:static void StaticFunc(){}private:static int StaticVar;inline static int InlineStaticVar = 1; 	// since C++17
    };//.cpp
    int MyStaticClass::StaticVar = 0;
    
  2. const静态成员

    如果一个整形变量或者枚举类型变量用const进行修饰时,其能够直接在类中进行定义。如果 LiteralType 的静态数据成员声明为 constexpr,则必须在类定义中使用初始化器对其进行初始化,初始化器中的每个表达式都是常量表达式。

    struct X
    {const static int n = 1;const static int m{2}; // since C++11const static int k;
    };
    
如何使用静态成员

访问类的静态成员有两种方式:

  1. 通过限定符(qualified)

    MyStaticClass::Func();

  2. 通过成员访问表达式(.、->)

    MyStaticClass().Func();

最后

通过一步一步的对所写的代码进行优化,我们最后实现了比较完美的单例:代码简单、线程安全、用时初始化,这都是这个单例的优点。希望看到这里,各位看官朋友能够有所收获,谢谢。

创作不易,如果对您有帮助,烦请点赞、收藏、关注支持一下,也欢迎各位大佬指点,谢谢。

参考

  1. Storage class specifiers - cppreference.com
  2. static members - cppreference.com
  3. 类的私有private构造函数 ,为什么要这样做 - onewayheaven - 博客园 (cnblogs.com)
  4. c++ - How static function is accessing private member function(constructor) of a class - Stack Overflow
  5. Initialization - cppreference.com
  6. C++11实现线程安全的单例模式(使用std::call_once)_c++11 线程安全-CSDN博客
  7. C++和双重检查锁定模式(DCLP)的风险_dclp认证-CSDN博客

相关文章:

c++单例实践

C单例实践 在日常开发中&#xff0c;虽然太多的单例调用会让代码的耦合度变高&#xff0c;但是例如日志类这种&#xff0c;单例模式就变得非常有。所以这篇文章为大家介绍static 关键字相关知识以及如何实现自己的C单例类。 static关键字 首先让我们请出今天的主角: static。…...

SQL注入实例(sqli-labs/less-9)

0、初始页面 1、爆库名 使用python脚本 def inject_database1(url):name for i in range(1, 20):low 32high 128mid (low high) // 2while low < high:payload "1 and if(ascii(substr(database(),%d,1)) > %d ,sleep(2),0)-- " % (i, mid)res {"…...

http不同类型方法的作用,get和post区别

在HTTP协议中&#xff0c;不同的请求方法用于不同的操作。常见的HTTP方法包括GET、POST、PUT、DELETE、HEAD、OPTIONS、PATCH等&#xff0c;每种方法有其特定的作用。 常见的HTTP方法及其作用 1. GET - **作用**: 从服务器请求指定资源。GET方法通常用于获取数据而不会修改数据…...

# 利刃出鞘_Tomcat 核心原理解析(二)

利刃出鞘_Tomcat 核心原理解析&#xff08;二&#xff09; 一、 Tomcat专题 - Tomcat架构 - HTTP工作流程 1、Http 工作原理 HTTP 协议&#xff1a;是浏览器与服务器之间的数据传送协议。作为应用层协议&#xff0c;HTTP 是基于 TCP/IP 协议来传递数据的&#xff08;HTML文件…...

美团秋招笔试第三题(剪彩带)求助帖

题目描述及代码如下。 我使用模拟打表法&#xff0c;示例通过了&#xff0c;但是提交通过率为0。诚心求教。欢迎补充题目&#xff0c;或者有原题链接更好~。我觉得可能出错的点&#xff1a;int -> long long ?或者一些临界条件。 /* 美团25毕业秋招第三题&#xff0c;做题…...

LeetCode 算法:最小栈 c++

原题链接&#x1f517;&#xff1a;最小栈 难度&#xff1a;中等⭐️⭐️ 题目 设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推…...

【解压既玩】PS3模拟器v0.0.32+战神3+战神升天+各存档 整合包 ,完美不死机,没有BUG,旷世神作,强力推荐

战神3是圣莫尼卡公司的大作&#xff0c;PS3 上必玩的游戏之一。 本文收集了战神3和升天两作&#xff0c;附存档&#xff0c;完美不死机&#xff0c;没有BUG&#xff0c;强烈推荐。 解压即玩。 立即下载&#xff1a;【chumenx.com】【解压既玩】PS3模拟器v0.0.32战神3战神升天…...

bootstrap- X-editable 行内编辑

前面不要忘记引入editable {field: weigh, title: __(Weigh),editable: {type: text,url: "api/cat/editWeigh",validate: function (v) {if($.trim(v) ) return 值不能为空;if(!$.isNumeric(v)) return 值只能为数字;if(v<0 || v%1!0) return 值必需为正整数;}…...

【LabVIEW学习篇 - 12】:通知器

文章目录 通知器案例一案例二案例三&#xff08;在不同VI中用同一个通知器&#xff09; 通知器 同步技术&#xff1a;同步技术用来解决多个并行任务之间的同步或通信问题。 通知器比较适合一对多的操作&#xff0c;类似于广播&#xff0c;一点发出的通知消息&#xff0c; 其它…...

Oracle一对多(一主多备)的DG环境如何进行switchover切换?

本文主要分享Oracle一对多(一主多备)的DG环境的switchover切换&#xff0c;如何进行主从切换&#xff0c;切换后怎么恢复正常同步&#xff1f; 1、环境说明 本文的环境为一主两备&#xff0c;数据库版本为11.2.0.4&#xff0c;主要信息如下&#xff1a; 数据库IPdb_unique_n…...

【浏览器插件】Chrome扩展V3版本

前言&#xff1a;Chrome从2022年6月开始&#xff0c;新发布插件只接受V3版。2024年V2版已从应用商店下架。 浏览器扩展插件开发API文档 chrome官网&#xff08;要翻墙&#xff09;&#xff1a; https://developer.chrome.com/docs/extensions/mv3 MDN中文&#xff1a;https:/…...

编码器信号干扰问题、编码器选型

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 前言一、屏蔽技术1.静电屏蔽:2.低频磁屏蔽:3.电磁屏蔽:4.减少“天线” 二、增量编码器的信号选择三、信号电缆选择四、…...

Unity入门5——材质

创建材质 点击Assets → Create → Material&#xff0c;得到一个默认材质球的副本。 使用材质 直接把材质球拖拽到物体上&#xff0c;或设置mesh renderer组件下的Materials 数组中第一个元素...

C的温故而知新:存储类别、链接和内存管理(C Primer Plus第十二章)

存储类别、链接和内存管理 这一章主要涉及到的是一些偏概念的东西&#xff0c;基本上偏向于自己去理解这部分内容。很好地理解这一章可以更好地控制程序&#xff0c;合理的利用内存存储数据。 C语言提供了多种不同的模型或存储类别在内存中存储数据。作用域有块作用域、函数作…...

SpringBoot统一功能处理——统一数据返回格式

目录 一、简单使用 二、存在的问题描述 三、优点 一、简单使用 统一的数据返回格式使用 ControllerAdvice 和 ResponseBodyAdvice 的方式实现 ControllerAdvice 表示控制器通知类。 添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接口&#xff0c;并在类上添加 …...

Milvus 实践(2) --- 2.4.x 安装,脚本分析,数据存储解析

目录 背景 Milvus2.4.x安装脚本分析 etcd组件 container_name image 参数 注意问题 environment volumes 实体化 command 参数 注意事项 healthcheck 参数 作用 下载 minio组件 container_name image 参数 注意事项 environment 参数 ports 参数 注…...

【蛋疼c++】千万别用std::wifstream读取Unicode UTF16文件

上当了。 最近程序要和 Jscript / activex 脚本通信。 ActiveX这玩意&#xff0c;导出文件&#xff0c;如果是UTF8导出&#xff0c;会出现莫名异常&#xff1a;写一半直接退出。或许是系统语言设置的问题。 但是切换为utf16&#xff08;unicode&#xff09;导出就没有问题&a…...

[算法] 第二集 二叉树中的深度搜索

深度优先遍历&#xff08;DFS&#xff0c;全称为 Depth First Traversal&#xff09;&#xff0c;是我们树或者图这样的数据结构中常⽤的 ⼀种遍历算法。这个算法会尽可能深的搜索树或者图的分支&#xff0c;直到⼀条路径上的所有节点都被遍历 完毕&#xff0c;然后再回溯到上…...

放弃使用外键时,sequelize 应该怎么使用?

在使用 Sequelize 时&#xff0c;如果想放弃使用外键&#xff0c;但仍然希望在模型之间建立关联&#xff0c;可以通过设置 constraints 选项为 false 来实现。这允许你定义模型之间的关系&#xff0c;而不在数据库中创建外键约束。以下是具体的实现步骤&#xff1a; 定义没有外…...

Microsoft GraphRAG 输出的配置信息

Microsoft GraphRAG 输出的配置信息 {"llm": {"api_key": "REDACTED, length 9","type": "oci_genai_chat","model": "cohere.command-r-plus","max_tokens": 4000,"temperature"…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...

elementUI点击浏览table所选行数据查看文档

项目场景&#xff1a; table按照要求特定的数据变成按钮可以点击 解决方案&#xff1a; <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...

Python常用模块:time、os、shutil与flask初探

一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...

Vue3 PC端 UI组件库我更推荐Naive UI

一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用&#xff0c;前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率&#xff0c;还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库&#xff08;Naive UI、Element …...