局部静态变量实现的单例存在多个对象

文章目录

  • 背景
  • 测试代码
  • 运行测试
    • 尝试打开编译器优化
    • 进一步分析

背景

业务中出现日志打印失效,发现是因为管理日志对象的单例在运行过程中存在了多例的情况。下面通过还原业务场景来分析该问题。

测试代码

/* A.h */
#ifndef CALSS_A
#define CALSS_A

#include <iostream>
#include <cstddef>
class A {
public:
    static A& GetInstance();

    void SetNum(size_t num);
    size_t GetNum();

private:
    size_t m_num {0U};
};
#endif
/* A.cpp */
#include "A.h"
A& A::GetInstance()
{
    static A ins;
    std::cout << "A " << &ins << std::endl;
    return ins;
}

void A::SetNum(size_t num)
{
    m_num = num;
}

size_t A::GetNum()
{
    return m_num;
}
/* A2.h */
#ifndef CALSS_A_2
#define CALSS_A_2

#include <iostream>
#include <cstddef>


class A {
public:
    static A& GetInstance()
    {
        static A ins2;
        std::cout << "A2 " << &ins2 << std::endl;
        return ins2;
    }

    void SetNum(size_t num) 
    {
        m_num = num;
    }
    size_t GetNum()
    {
        return m_num;
    }

private:
    size_t m_num {0U};
};
#endif
/* B.h */
#ifndef CLASS_B
#define CLASS_B

#include <cstddef>

class B {
public:
    B();
    size_t GetNum();
};

#endif

/* B.cpp */
#include "B.h"
#include "A2.h"

B::B()
{
    A::GetInstance().SetNum(100U);
}

size_t B::GetNum()
{
    return A::GetInstance().GetNum();
}
#include "A.h"
#include "B.h"
#include <iostream>

int main()
{
    B b;
    A::GetInstance().SetNum(10U);
    std::cout << A::GetInstance().GetNum() << std::endl;
    std::cout << b.GetNum() << std::endl;
    return 0;
}

运行测试

通过简化的代码模拟业务中实际的依赖关系:头文件A.h中定义了类A,单例的实现在A.cpp中,生成动态库a;头文件A2.h中同样也定义了类A,单例的视线在头文件中,被B.cpp引用,生成动态库b;可执行文件a.out中会同时调用动态库a和动态库b中的接口,在实际业务中出现了多例的情况。
在这里插入图片描述

g++ A.cpp -I . -fpic -shared -o liba.so -O2
g++ B.cpp -I . -fpic -shared -o libb.so -O2
g++ main.cpp -L . -lb -la -I . -O2

运行结果显示,只存在单例,获取到的是A2.h中定义的对象(libb.so)。

A2 0x7fbcdfb19068
A2 0x7fbcdfb19068
A2 0x7fbcdfb19068
10
A2 0x7fbcdfb19068
10

调整二进制动态库链接的顺序,获取到的是A.cpp中定义的对象(liba.so)。从目前测试情况分析,不会出现多例的情况,但是具体使用的符号,跟动态库链接的顺序有关系,二进制中会使用先链接的动态库的符号

g++ main.cpp -L . -la -lb -I .

A 0x7f99ef74f058
A 0x7f99ef74f058
A 0x7f99ef74f058
10
A 0x7f99ef74f058
10

从符号表分析:使用readelf读取动态库和二进制的符号表,动态库b中既存在单例获取成员函数A::GetInstance()的弱符号,又存在全局唯一对象A::GetInstance()::ins2的符号。结合上述现象,先链接动态库b时,加载全局唯一对象A::GetInstance()::ins2的内存地址,后续获取到的单例都是该内存地址;后链接动态库b,弱符号A::GetInstance()会被a库中的强符号覆盖,因此获取到的单例是A.cpp中定义的对象。
readelf查看符号表-1

尝试打开编译器优化

前面证明链接时候的顺序不同,会加载不同内存地址的对象,但是在运行过程中还是单例。现在猜测运行过程中出现多例情况可能跟编译器的优化有关。因此,舱室打开编译的优化选项,重读上面的测试。

g++ A.cpp -I . -fpic -shared -o liba.so -O2
g++ B.cpp -I . -fpic -shared -o libb.so -O2
g++ main.cpp -L . -lb -la -I . -O2

运行结果显示,出现了多例的现象。

A2 0x7f019f611068
A 0x7f019f60c068
A 0x7f019f60c068
10
A2 0x7f019f611068
100

从符号表分析:与未打开编译器优化前最大的区别在于动态库b中单例获取成员函数A::GetInstance()的弱符号不见了,故动态库b中源码加载全局唯一对象A::GetInstance()::ins2的内存地址,动态库a中源码加载的是通过A::GetInstance()获取的对象的地址,两者地址不同。
因此,可以解释为什么在运行过程中出现了双例的情况。
readelf获取符号表-2

进一步分析

动态库b中单例获取成员函数A::GetInstance()的弱符号不见了的原因:

头文件中定义的函数,特别是内联函数和模板函数,在编译和链接过程中通常会被展开或优化掉,不会产生独立的符号。

无论是链接时会存在双例的情况,还是运行时会存在双例的情况,都是不符合预期的。因此,如何避免?
很简单,单例的实现放在cpp中。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何从 Windows 11/10/8.1/8/7 恢复已删除的视频

意外删除了视频或格式化了 SD 卡/硬盘&#xff1f;没有备份已删除的视频&#xff1f;别担心&#xff0c;我们有解决方案来恢复 Windows 11、10 中已删除的视频并处理这种糟糕的情况。 但在了解如何恢复已删除的视频和视频恢复应用程序之前&#xff0c;请知道 Windows 会为您提…

IDEA与通义灵码的智能编程之旅

1 概述 本文主要介绍在IDEA中如何安装和使用通义灵码来助力软件编程,从而提高编程效率,创造更大的个人同企业价值。 2 安装通义灵码 2.1 打开IDEA插件市场 点击IDEA的设置按钮,下拉选择Plugins,如下: 2.2 搜索通义灵码 在搜索框中输入“通义灵码”,如下: 2.3 安…

【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【17】认证服务01—短信/邮件/异常/MD5

持续学习&持续更新中… 守破离 【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【17】认证服务01 环境搭建验证码倒计时短信服务邮件服务验证码短信形式&#xff1a;邮件形式&#xff1a; 异常机制MD5参考 环境搭建 C:\Windows\System32\drivers\etc\hosts 192.168.…

rsyslog日志转发

前言 Rsyslog可用于接受来自各种来源(本地和网络)的输入&#xff0c;转换它们&#xff0c;并将结果输出到不同&#xff08;通过模板和filter过滤&#xff09;的目的地&#xff08;目录文件中&#xff09; rsyslog是一个开源工具&#xff0c;被广泛用于Linux系统以通过TCP/UDP…

树莓派5安装冬瓜HAOS教程

原文来自瀚思彼岸和hasshome 一、安装前准备 &#xff08;1&#xff09;软件 1、树莓派烧录软件Imager 2、冬瓜HAOS镜像 &#xff08;2&#xff09;硬件 1、树莓派5 2、TF卡&#xff08;SanDisk Extreme PRO 64GB U3 A2 V30 4k&#xff09; 3、读卡器 4、键盘和鼠标 5、显…

550kg级大载重长航时无人机直升机技术详解

550kg级大载重长航时无人机直升机&#xff0c;作为一种高性能的无人机系统&#xff0c;具备了多项先进的技术特点&#xff0c;以满足高海拔、高寒等复杂环境下的应用需求。这些无人机直升机通常具备高载重、长航时、强适应性、高可靠性和良好的任务拓展性。 设备由无人直升机平…

ctfshow-web入门-文件上传(web151-web160)

目录 1、web151 2、web152 3、web153 4、web154 5、web155 6、web156 7、web157 8、web158 9、web159 10、web160 1、web151 试了下前端只能传 png 后缀的 将一句话木马改成 png 后缀&#xff0c;上传后用 burpsuite 抓包 绕过前端检测后&#xff0c;改回 php 后缀&am…

(南京观海微电子)——MOS管原理及应用区别

MOS管&#xff1a; 全称为金属氧化物半导体场效应管&#xff08;Metal Oxide Semiconductor Field Effect Transistor&#xff09;&#xff0c;也被称为MOSFET&#xff08;Metal-Oxide-Semiconductor Field-Effect Transistor&#xff09;。它是一种半导体器件&#xff0c;常用…

[数据结构] 基于选择的排序 选择排序堆排序

标题&#xff1a;[数据结构] 基于选择的排序 选择排序&&堆排序 水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 &#xff08;一&#xff09;选择排序 实现&#xff1a;(默认从小到大排序) 优化后实现方法&#xff1a; &#xff08;二&#xff09;堆排序…

latex英文转中文word,及一些latex相关工具分享

前言&#xff1a;想要转换latex生成的英文pdf文件为中文word文件 一、主要步骤 1、文字翻译&#xff1a;直接使用谷歌翻译等辅助将英文翻译成中文即可&#xff1b; 2、图片&#xff1a; 使用latex时一般保存的.png&#xff0c;.bmp格式图片可以直接插入word, 但是.eps或者 .p…

基于Android Studio零食工坊

目录 项目介绍 图片展示 运行环境 获取方式 项目介绍 用户 可以浏览商品 &#xff0c; 查询商品 &#xff0c; 加入购物车 &#xff0c; 结算商品 &#xff0c; 查看浏览记录 &#xff0c; 修改密码 &#xff0c; 修改个人信息 &#xff0c; 查询订单 管理员 能够实现商品的…

AIGC专栏12——EasyAnimateV3发布详解 支持图文生视频 最大支持960x960x144帧视频生成

AIGC专栏12——EasyAnimateV3发布详解 支持图&文生视频 最大支持960x960x144帧视频生成 学习前言项目特点生成效果相关地址汇总项目主页Huggingface体验地址Modelscope体验地址源码下载地址 EasyAnimate V3详解技术储备Diffusion Transformer (DiT)Hybrid Motion ModuleU-V…

分布式整合

一、分布式架构介绍 什么是分布式系统 分布式系统指一个硬件或软件组件分布在不同的网络计算机上&#xff0c;彼此之间仅仅通过消息传递进行通信和协调的系统。 通俗的理解&#xff0c;分布式系统就是一个业务拆分成多个子业务&#xff0c;分布在不同的服务器节点&#xff0…

测试环境:使用OpenSSL生成证书并配置Https

文章目录 需求1、安装OpenSSL1.1、安装包下载1.2、安装&#xff08;以window 64位为例&#xff09;1.3、配置环境变量&#xff08;非必须&#xff09; 2、生成证书2.1、新建文件夹2.2、生成根证书2.2.1、生成私钥2.2.2、生成根证书&#xff0c;并且自签名 2.3、服务端证书生成2…

JDBC的基本认识

前提 在了解和学习JDBC之前&#xff0c;大家 已经学习过 java语言 和数据库的基本知识了&#xff0c;今天这篇博客的核心&#xff0c;就是告诉大家 &#xff0c;jdbc 是连接java编译器和数据库&#xff0c;是使用java对数据库进行操作的。 正文 JDBC简介 概念 JDBC的本质 1…

解决微信读书和Apple Books导入epub电子书不显示图片的问题

title: 解决微信读书和Apple Books导入epub电子书不显示图片的问题 tags: 个人成长 categories:杂谈 最近找到一本很喜欢的书的电子版的epub版&#xff0c;发现无论是导入微信读书&#xff0c;还是Apple家的Books, 都无法正常显示图片。 于是我用calibre打开epub电子书&#x…

昇思25天学习打卡营第10天 | 自然语言处理:RNN实现情感分类

1. RNN实现情感分类 1.2 概述 情感分类是自然语言处理中的经典任务&#xff0c;是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型&#xff0c;实现如下的效果&#xff1a; 输入: This film is terrible 正确标签: Negative(负面) 预测标签: Negative输…

nacos-sdk-python——Python版本Nacos客户端

Nacos&#xff08;Naming and Configuration Service&#xff09;是阿里巴巴开源的一款动态服务发现、配置管理和服务管理平台。它主要用于解决微服务架构中服务发现和配置管理的问题&#xff0c;提供了一站式解决方案。以下是 Nacos 的几个关键功能&#xff1a; 服务发现和健康…

C++模板元编程(一)——可变参数模板

这个系列主要记录C模板元编程的常用语法 文章目录 引言语法应用函数模板可变参数的打印可变参数的最小/最大函数 类模板 参考文献 引言 在C11之前&#xff0c;函数模板和类模板只支持含有固定数量的模板参数。C11增强了模板功能&#xff0c;允许模板定义中包含任意个(包括0个)…

保研复习 | 数据结构

目录 CH1 绪论☆ 数据项、数据元素、数据结构☆ 逻辑结构和存储结构的区别☆ 顺序存储结构和链式存储结构的比较☆ 算法的重要特性☆ 算法的复杂度 CH2 线性表☆ 单链表 CH3 栈、队列和数组☆ 栈和堆是什么&#xff1f;☆ 栈在括号匹配中的应用☆ 栈在表达式求值中的应用☆ …