C++ 单例模式两种实现方式
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>一、饿汉模式 (Eager Initialization)</li><ul class="second_class_ul"><li>特点:类加载时就立即创建实例</li><li>实现代码:</li><li>优点:</li><li>缺点:</li></ul><li>二、饱汉模式 (Lazy Initialization)</li><ul class="second_class_ul"><li>特点:第一次使用时才创建实例</li><li>基础版本(非线程安全):</li><li>线程安全版本(双检锁):</li><li>C++11 之后更简洁的线程安全版本:</li></ul><li>三、对比表格</li><ul class="second_class_ul"><li>性能对比数据</li></ul><li>四、选择建议</li><ul class="second_class_ul"><li>使用饿汉模式当:</li><li>使用饱汉模式当:</li></ul></ul></div><p class="maodian"></p><h2>一、饿汉模式 (Eager Initialization)</h2><p class="maodian"></p><h3>特点:类加载时就立即创建实例</h3>
<p class="maodian"></p><h3>实现代码:</h3>
<div class="jb51code"><pre class="brush:cpp;">class Singleton {
private:
static Singleton* instance;// 静态成员
Singleton() {}// 私有构造函数
~Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton* getInstance() {
return instance;// 直接返回已创建的实例
}
};
// 关键:类外初始化时直接创建实例
Singleton* Singleton::instance = new Singleton();</pre></div>
<p class="maodian"></p><h3>优点:</h3>
<ul><li><strong>线程安全</strong>:实例在程序启动时就创建,多线程访问时不会有竞争问题</li><li><strong>简单直观</strong>:实现简单,不需要考虑线程同步</li><li><strong>调用效率高</strong>:<code>getInstance()</code> 直接返回指针,没有锁开销</li></ul>
<p class="maodian"></p><h3>缺点:</h3>
<ul><li><strong>资源占用早</strong>:即使不使用也会占用内存</li><li><strong>启动慢</strong>:如果构造复杂,会影响程序启动速度</li><li><strong>依赖顺序问题</strong>:多个饿汉单例的初始化顺序不确定</li></ul>
<p class="maodian"></p><h2>二、饱汉模式 (Lazy Initialization)</h2>
<p class="maodian"></p><h3>特点:第一次使用时才创建实例</h3>
<p class="maodian"></p><h3>基础版本(非线程安全):</h3>
<div class="jb51code"><pre class="brush:cpp;">class Singleton {
private:
static Singleton* instance;
Singleton() {}
~Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton* getInstance() {
if (instance == nullptr) { // 第一次检查
instance = new Singleton(); // 创建实例
}
return instance;
}
};
Singleton* Singleton::instance = nullptr;// 初始化为空</pre></div>
<p class="maodian"></p><h3>线程安全版本(双检锁):</h3>
<div class="jb51code"><pre class="brush:cpp;">#include <mutex>
class Singleton {
private:
static Singleton* instance;
static std::mutex mtx;
Singleton() {}
~Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton* getInstance() {
if (instance == nullptr) { // 第一次检查(避免每次加锁)
std::lock_guard<std::mutex> lock(mtx); // 加锁
if (instance == nullptr) { // 第二次检查(确保只创建一次)
instance = new Singleton();
}
}
return instance;
}
};
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;</pre></div>
<p class="maodian"></p><h3>C++11 之后更简洁的线程安全版本:</h3>
<div class="jb51code"><pre class="brush:cpp;">class Singleton {
private:
Singleton() {}
~Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& getInstance() {
static Singleton instance;// C++11 保证静态局部变量初始化是线程安全的
return instance;
}
};</pre></div>
<p class="maodian"></p><h2>三、对比表格</h2>
<table><thead><tr><th>特性</th><th>饿汉模式</th><th>传统饱汉模式(双检锁)</th><th><strong>C++11静态局部变量</strong></th></tr></thead><tbody><tr><td><strong>创建时机</strong></td><td>程序启动时<br />(main函数之前)</td><td>第一次调用 <code>getInstance()</code> 时</td><td><strong>第一次调用 getInstance() 时</strong></td></tr><tr><td><strong>线程安全</strong></td><td>✅ 天然线程安全<br />(静态初始化阶段)</td><td>✅ 需要双检锁等机制<br />(手动实现)</td><td>✅ <strong>编译器保证线程安全</strong><br />(C++11标准规定)</td></tr><tr><td><strong>资源占用</strong></td><td>❌ 启动即占用内存<br />(即使不用)</td><td>✅ 使用时才占用内存</td><td>✅ <strong>使用时才占用内存</strong></td></tr><tr><td><strong>启动速度</strong></td><td>⚠️ 可能较慢<br />(如果构造复杂)</td><td>✅ 启动快</td><td>✅ <strong>启动快</strong></td></tr><tr><td><strong>实现复杂度</strong></td><td>✅ 简单</td><td>❌ 复杂<br />(需正确实现双检锁)</td><td>✅ <strong>非常简洁</strong><br />(一行代码)</td></tr><tr><td><strong>性能开销</strong></td><td>✅ 调用无锁开销</td><td>⚠️ 有锁检查开销<br />(即使已初始化)</td><td>✅ <strong>几乎无开销</strong><br />(编译器优化)</td></tr><tr><td><strong>销毁控制</strong></td><td>❌ 需手动管理</td><td>❌ 需手动管理</td><td>✅ <strong>自动管理</strong><br />(程序结束时)</td></tr><tr><td><strong>C++版本要求</strong></td><td>C++98</td><td>C++98(需要锁)<br />C++11(原子操作)</td><td><strong>C++11及以上</strong></td></tr><tr><td><strong>内存泄漏风险</strong></td><td>⚠️ 需正确释放</td><td>⚠️ 需正确释放</td><td>✅ <strong>无泄漏风险</strong></td></tr><tr><td><strong>代码示例</strong></td><td><code>cpp<br>static T* instance = new T();<br></code></td><td><code>cpp<br>if (!instance) {<br> lock();<br> if (!instance) {<br> instance = new T();<br> }<br>}<br></code></td><td><code>cpp<br>static T instance;<br>return instance;<br></code></td></tr><tr><td><strong>适用场景</strong></td><td>1. 实例小、构造简单<br />2. 程序必用<br />3. 多线程环境<br />4. C++98项目</td><td>1. 实例大、构造复杂<br />2. 可能不用<br />3. C++98项目<br />4. 需要延迟初始化</td><td><strong>绝大多数场景</strong><br />1. C++11+项目<br />2. 需要延迟初始化<br />3. 要求代码简洁<br />4. 需要自动清理</td></tr></tbody></table>
<p class="maodian"></p><h3>性能对比数据</h3>
<table><thead><tr><th>操作</th><th>饿汉模式</th><th>双检锁饱汉</th><th>C++11静态局部变量</th></tr></thead><tbody><tr><td><strong>第一次调用</strong></td><td>0-10 ns</td><td>50-100 ns</td><td>20-50 ns</td></tr><tr><td><strong>后续调用</strong></td><td>0-5 ns</td><td>10-20 ns</td><td>0-5 ns</td></tr><tr><td><strong>内存占用</strong></td><td>立即占用</td><td>延迟占用</td><td>延迟占用</td></tr><tr><td><strong>线程竞争</strong></td><td>无竞争</td><td>有锁竞争</td><td>首次有竞争,后续无</td></tr></tbody></table>
<p class="maodian"></p><h2>四、选择建议</h2>
<p class="maodian"></p><h3>使用饿汉模式当:</h3>
<ul><li>单例对象小,构造简单</li><li>程序运行期间一定会用到</li><li>对性能要求高,希望无锁访问</li><li>多线程环境下不想处理同步问题</li></ul>
<p class="maodian"></p><h3>使用饱汉模式当:</h3>
<ul><li>单例对象大,构造复杂或耗时</li><li>可能整个程序都不会用到该实例</li><li>对启动速度有要求</li><li>使用 C++11 或以上版本(可用静态局部变量)</li></ul>
<p>到此这篇关于C++ 单例模式两种实现方式的文章就介绍到这了,更多相关C++ 单例模式内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>C++ 单例模式的几种实现方式研究</li><li>C++实现单例模式日志输出详解</li><li>C++单例模式实现线程池的示例代码</li><li>利用C++单例模式实现高性能配置管理器</li><li>C++深入详解单例模式与特殊类设计的实现</li><li>C++单例模式的几种实现方法详解</li><li>C++实现单例模式的自动释放</li><li>C++实现 单例模式实例详解</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]