局部靜態變量實現的單例存在多個對象

文章目錄

  • 背景
  • 測試代碼
  • 運行測試
    • 嘗試打開編譯器優化
    • 進一步分析

背景

業務中出現日志打印失效,發現是因為管理日志對象的單例在運行過程中存在了多例的情況。下面通過還原業務場景來分析該問題。

測試代碼

/* 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.pswp.cn/diannao/42284.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/42284.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/42284.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

打造屬于自己的腳手架工具并發布到npm倉庫

一、創建項目 使用 npm init -y 創建項目創建項目入口文件 index.js在 package.json 中添加 bin 字段使用 npm link 命令將文件映射至全局&#xff0c;使可以在本地測試 zp 命令 // "zp" 為用于全局執行腳手架的命令&#xff0c;vue-cli中使用的是vue命令 "bi…

pyecharts可視化案例大全(11~20)

pyecharts可視化案例大全(11~20) 十一、設置動畫效果十二、直方圖帶視覺組件十三、設置漸變色(線性漸變)十四、設置漸變色(徑向漸變)十五、設置分割線十六、設置分隔區域十七、面積圖十八、堆疊面積圖十九、自定義線樣式二十、折線圖平滑處理十一、設置動畫效果 在圖表加載前…

【AI原理解析】—主成分分析(PCA)原理

目錄 一、PCA的思想 二、PCA的步驟 三、關鍵概念 四、PCA的優勢與應用 PCA&#xff08;主成分分析&#xff0c;Principal Component Analysis&#xff09;是一種廣泛使用的數據降維算法&#xff0c;它通過線性變換將原始數據轉換為一組各維度線性無關的表示&#xff0c;從而…

iOS應用的內存優化

對一個 iOS 項目進行內存優化&#xff0c;可以從多個方面入手&#xff0c;確保應用在不同場景下都能高效穩定地運行。以下是一些具體的內存優化措施和詳細說明&#xff1a; 1. 自動引用計數&#xff08;ARC&#xff09;管理 1.1 避免循環引用 循環引用會導致內存泄漏。使用 …

低代碼平臺的設計模式介紹

低代碼平臺是一種快速交付應用程序的開發工具&#xff0c;主要通過圖形拖拽用戶界面、應用配置界面&#xff0c;使開發者能夠以最少的手動編碼&#xff0c;或者不需要代碼快速交付應用。這種平臺的核心優勢在于提高開發速度和降低技術門檻&#xff0c;使得非技術背景的用戶也能…

基于java+springboot+vue實現的旅游管理系統(文末源碼+lw+ppt)23-402

研究的內容 當下流行的WPS、Word等辦公軟件成為了人們耳熟能詳的系統&#xff0c;但一些更加專業性、性能更加強大的網絡信息工具被人們“埋沒”在互聯網的大海中。甘肅旅游管理系統是一個便于用戶查看熱門景點、酒店信息、推薦線路、旅游攻略、景點資訊等&#xff0c;管理員進…

【Python基礎篇】你了解python中運算符嗎

文章目錄 1. 算數運算符1.1 //整除1.2 %取模1.3 **冪 2. 賦值運算符3. 位運算符3.1 &&#xff08;按位與&#xff09;3.2 |&#xff08;按位或&#xff09;3.3 ^&#xff08;按位異或&#xff09;3.4 ~&#xff08;按位取反&#xff09;3.5 <<&#xff08;左移&#…

HTML 【實用教程】(2024最新版)

核心思想 —— 語義化 【面試題】如何理解 HTML 語義化 ?僅通過標簽便能判斷內容的類型&#xff0c;特別是區分標題、段落、圖片和表格 增加代碼可讀性&#xff0c;讓人更容易讀懂對SEO更加友好&#xff0c;讓搜索引擎更容易讀懂 html 文件的基本結構 html 文件的文件后綴為 …

【高錄用、快檢索、過往5屆均已檢索、SPIE 出版】第六屆無線通信與智能電網國際會議(ICWCSG 2024)

隨著科技的飛速發展和能源需求的日益增長&#xff0c;智能電網技術逐漸成為電力行業的重要發展方向。與此同時&#xff0c;無線通信技術在近年來也取得了顯著的進步&#xff0c;為智能電網的發展提供了強有力的支持。為了進一步推動無線通信與智能電網的結合與發展&#xff0c;…

Vue3 對于內嵌Iframe組件進行緩存

1&#xff1a;應用場景 對于系統內所有內嵌iframe 的頁面均通過同一個路由/iframe, 在router.query內傳入不同src 參數&#xff0c;在同一組件內顯示iframe 內嵌頁面&#xff0c;對這些頁面分別進行緩存。主要是通過v-show 控制顯示隱藏從而達到iframe 緩存邏輯 2&#xff1a…

Github 2024-07-03 C開源項目日報 Top9

根據Github Trendings的統計,今日(2024-07-03統計)共有9個項目上榜。根據開發語言中項目的數量,匯總情況如下: 開發語言項目數量C項目9Java項目1Python項目1顯示和控制你的 Android 設備 創建周期:2416 天開發語言:C, Java協議類型:Apache License 2.0Star數量:105222 個…

學IT上培訓班真的有用嗎?

在學習IT技術的過程中&#xff0c;你是否也被安利過各種五花八門的技術培訓班&#xff1f;這些培訓班都是怎樣向你宣傳的&#xff0c;你又對此抱有著怎樣的態度呢&#xff1f;在培訓班里學技術&#xff0c;真的有用嗎&#xff1f; 一、引入話題 IT行業是一個快速發展和不斷變化…

C++初學者指南-4.診斷---未定義行為檢測器

C初學者指南-4.診斷—未定義行為檢測器 未定義行為檢測器(UBSAN) 適用編譯器&#xff1a;clang,g在運行時檢測許多類型的未定義行為 解引用空指針從未對齊的指針讀取整數溢出被0除 … 在代碼中加入額外的指令:在調試構建中增加運行時約25% 示例&#xff1a;有符號整形溢出 …

Git在多人開發中的常見用例

前言 作為從一個 svn 轉過來的 git 前端開發&#xff0c;在經歷過git的各種便捷好處后&#xff0c;想起當時懵懂使用git的膽顫心驚&#xff1a;總是害怕用錯指令&#xff0c;又或者遇到報錯就慌的場景&#xff0c;想起當時查資料一看git指令這么多&#xff0c;看的頭暈眼花&am…

深度學習原理與Pytorch實戰

深度學習原理與Pytorch實戰 第2版 強化學習人工智能神經網絡書籍 python動手學深度學習框架書 TransformerBERT圖神經網絡&#xff1a; 技術講解 編輯推薦 1.基于PyTorch新版本&#xff0c;涵蓋深度學習基礎知識和前沿技術&#xff0c;由淺入深&#xff0c;通俗易懂&#xf…

家里老人能操作的電視直播軟件,目前能用的免費看直播的電視軟件app,適合電視和手機使用!

2024年許多能看電視直播的軟件都不能用了&#xff0c;家里的老人也不會手機投屏&#xff0c;平時什么娛樂都沒有了&#xff0c;這真的太不方便了。 很多老人并不喜歡去買一個廣電的機頂盒&#xff0c;或者花錢拉有線電視。 現在的電視大多數都是智能電視&#xff0c;所以許多電…

Redis基本命令源碼解析-字符串命令

1. set 用于將kv設置到數據庫中 2. mset 批量設置kv mset (msetnx) key1 value1 key2 value2 ... mset:msetCommand msetnx:msetnxCommand msetCommand和msetnxCommand都調用msetGenericCommand 2.1 msetGenericCommand 如果參數個數為偶數,則響應參數錯誤并返回 如果…

【項目日記(一)】夢幻筆耕-數據層實現

?博主主頁: 33的博客? ??文章專欄分類:項目日記?? &#x1f69a;我的代碼倉庫: 33的代碼倉庫&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;關注我帶你了解更多項目內容 目錄 1.前言2.后端模塊3數據庫設計4.mapper實現4.1UserInfoMapper4.2BlogMapper 5.總結 1.…

硬件開發筆記(二十四):貼片電容的類別、封裝介紹,AD21導入貼片電容、原理圖和封裝庫3D模型

若該文為原創文章&#xff0c;轉載請注明原文出處 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140241817 長沙紅胖子Qt&#xff08;長沙創微智科&#xff09;博文大全&#xff1a;開發技術集合&#xff08;包含Qt實用技術、樹莓派、三維、OpenCV…

存儲結構與管理磁盤

前言&#xff1a;本博客僅作記錄學習使用&#xff0c;部分圖片出自網絡&#xff0c;如有侵犯您的權益&#xff0c;請聯系刪除 目錄 一、一切從“/”開始 二、物理設備的命名規則 三、文件系統與數據資料 四、掛載硬件設備 五、添加硬盤設備 六、添加交換分區 七、磁盤容…