单例模式实现要点:
- 构造函数私有化 - 为避免其他程序过多建立该类对象,先禁止其他程序建立该类对象
- 类中创建一个本类对象 - 本类中自定义对象,让其他程序可以访问
- 提供方法获取到该对象 - 方便其他程序对自定义对象的访问
单例模式实现方式:
1.懒汉式(多线程不安全) - 需要时才进行实例化。
#include<iostream>
using namespace std;
#include <windows.h>//核心的本质就是,只能实例化一个实体
class Singleton {
private:Singleton() {}Singleton(Singleton&) = delete; //禁止使用Singleton& operator=(const Singleton&object) = delete; //禁止使用public:~Singleton() {}//获取一个实例static Singleton* getInstance() {//如果指针为空,则重新创造一个实例if (m_instance_ptr == nullptr) {m_instance_ptr = new Singleton;}//否则,说明已经创建过了,直接返回return m_instance_ptr;}void func(){cout << "成功实例化了!!!" << endl;}
private:static Singleton* m_instance_ptr;
};Singleton* Singleton::m_instance_ptr = nullptr;//在外部对指针进行初始化int main() {static Singleton* instance = Singleton::getInstance();instance->func();system("pause");return 0;
}
问题1:static Singleton* m_instance_ptr; 中的static有什么作用
在这个代码中,
static
关键字用于声明m_instance_ptr
成员变量为静态变量。静态变量是属于类而不是类的实例的变量,因此在整个程序运行期间只会有一个m_instance_ptr
变量的实例。这使得在getInstance()
方法中可以对该变量进行检查,确保只创建一个Singleton
类的实例。
问题2: c++ static是不是就只有一份数据,每次操作都会直接作用在本体上
是的,C++中的静态数据成员只有一份数据,不会随着对象的创建而分配新的内存空间。每次操作都会直接作用在这份数据上,不会因为对象的创建而产生新的副本。因此,静态数据成员可以被所有对象共享,并且可以在不创建对象的情况下直接访问和修改。
2.懒汉式(多线程安全)-进行加锁
#include <iostream>
#include <memory> // shared_ptr
#include <mutex> // mutexclass Singleton {
public:typedef std::shared_ptr<Singleton> Ptr;~Singleton() {std::cout << "析构函数!!!" << std::endl;}Singleton(Singleton&) = delete; //禁止使用Singleton& operator=(const Singleton&) = delete; //禁止使用static Ptr getInstance() {if (m_instance_ptr == nullptr) {std::lock_guard<std::mutex> auto_lock(m_mutex);if (m_instance_ptr == nullptr) {m_instance_ptr = std::shared_ptr<Singleton>(new Singleton);}}return m_instance_ptr;}void use() const {std::cout << "已经成功初始化了" << std::endl;}private:Singleton() {}static Ptr m_instance_ptr;static std::mutex m_mutex;
};
Singleton::Ptr Singleton::m_instance_ptr = nullptr;
std::mutex Singleton::m_mutex;int main() {Singleton::Ptr instance = Singleton::getInstance();instance->use();system("pause");return 0;
}
问题1:shared_ptr<Singleton>有什么作用
使用
std::shared_ptr<Singleton>
可以确保在程序中只有一个实例对象,并且可以方便地在需要时访问该实例对象。std::shared_ptr
是智能指针的一种,它可以自动管理对象的生命周期,确保在不再需要时正确地释放对象,避免内存泄漏。在这个示例中,std::shared_ptr<Singleton>
确保了只有一个Singleton
对象实例,并且在程序结束时正确地释放该实例对象。
问题2: 上面的代码加锁了,难道不用解锁吗
在上面的代码示例中,确实在获取单例对象实例时使用了
std::lock_guard<std::mutex>
来加锁,但是没有显式地解锁。这是因为std::lock_guard
是一个RAII(资源获取即初始化)类,它在构造时会自动锁住互斥量,在析构时会自动释放锁,即在作用域结束时会自动解锁。因此,在上面的代码中,当
std::lock_guard<std::mutex>
对象auto_lock
超出作用域时,会自动调用析构函数,从而释放锁,不需要显式地调用解锁操作。这样可以确保在任何情况下都能正确释放锁,避免忘记解锁而导致的死锁问题。
问题3:静态变量都需要在类外进行初始化吗?
在C++中,静态成员变量必须在类外进行定义和初始化。这是因为静态成员变量是属于类的,而不是属于类的实例,因此需要在类外进行定义和初始化。
在上面的代码中,
singleton::ptr
和singleton::m_mutex
都是singleton
类的静态成员变量,因此需要在类外进行初始化。静态成员变量的初始化必须在全局作用域进行,以确保在程序运行之前就完成了初始化。通过在全局作用域进行初始化,确保了静态成员变量在程序运行时已经被正确初始化,可以在类的静态成员函数或其他地方使用这些静态成员变量。
问题4: NULL和nullptr有什么区别
NULL
和nullptr
是C++中用于表示空指针的关键字,它们在一些方面有一些区别:
NULL
是在C语言中定义的宏,通常被定义为0
或者(void*)0
。在C++中,NULL
通常被定义为0
。因此,NULL
实际上是一个整数值的常量。
nullptr
是C++11引入的关键字,用于表示空指针常量。nullptr
是一个特殊的空指针值,不是整数类型,而是属于nullptr_t类型。因此,nullptr
可以更好地表示空指针,避免了一些潜在的问题。在C++11及以后的标准中,推荐使用
nullptr
来表示空指针,而不是NULL
。因为nullptr
具有更好的类型安全性,可以避免一些潜在的类型转换问题。综上所述,
nullptr
是C++11引入的更安全和更明确的表示空指针的关键字,推荐在新的C++代码中使用nullptr
来表示空指针。而NULL
仍然可以在一些老的代码中使用,但最好在新代码中使用nullptr
。
3.饿汉式-提前实例化
#include <iostream>class Singleton {
public:~Singleton() {std::cout << "析构函数!!!" << std::endl;}Singleton(Singleton&) = delete; //禁止使用Singleton& operator=(const Singleton&) = delete; //禁止使用static Singleton* getInstance() {return m_instance;}void use() const {std::cout << "已经实例化对象了!!!" << std::endl;}private:Singleton() {}static Singleton* m_instance;};//直接在外部就只实例化一个对象,避免的多创建的风险
Singleton* Singleton::m_instance = new Singleton;int main() {Singleton* instance = Singleton::getInstance();instance->use();system("pause");return 0;
}