单例模式中的懒汉模式

//懒汉模式
class Singleton
{
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            i_mutex.lock();
            if (instance == nullptr) {
                instance = new Singleton();
            }
            i_mutex.unlock();
        }
        return instance;
    };
    ~Singleton() {};
    static cleanup() { delete instance; instance = nullptr; };
private:
    Singleton() {};
    Singleton(const Singleton& s) = delete;
    Singleton& operator=(const Singleton& s) = delete;
private:
    static Singleton* instance;
    static std::mutex i_mutex;
};
Singleton* Singleton::instance = nullptr;
mutex Singleton::i_mutex;

单例模式中的饿汉模式

class SingletonHungry
{
public:
static SingletonHungry* getInstance() { return instance; };
~SingletonHungry() { delete instance; };
private:
SingletonHungry() {};
SingletonHungry(const SingletonHungry& s) = delete;
SingletonHungry& operator=(const SingletonHungry& s) = delete;
private:
static SingletonHungry* instance;
};
SingletonHungry* SingletonHungry::instance = new SingletonHungry();

1、单例模式中,类的构造函数为什么是private的?

权限修饰符private的作用:保证类的构造函数不能出现在类的外部,换句话说,你不能在类的外部,比如在主线程main函数中来直接调用构造函数来构造对象。另一种情况,也是最常见的,我们通常使用关键词public来修饰构造函数,这样的构造函数是直接暴露给类的外部的,类的外部是可以多次调用这样的构造函数来创建多个对象,比如在主线程main函数中多次调用构造函数来创建多个对象。

2、类的构造函数为什么不能使用static关键字?

类的构造函数不能是static的,因为类的构造函数的参数中有一个隐式的指向对象本身的this指针,通过该指针你可以访问内部的成员变量。当你使用static关键字来修饰类的一个成员函数,那么编译器在编译该成员函数时,编译器就不会将隐式的this指针添加到该成员函数的形参的末尾。

对于关键字static的理解

注意,static关键字只作用于当前变量,意思是将当前变量定义在静态存储区。
举个例子:

#include<iostream>

class Test {
    public:
        int static* si1;//static关键词作用于当前变量,即使这个变量是一个指针变量
        int static* si2;
        int static* si3;
    public:
        Test() {
        }
        void print() {
            std::cout << &si1 << ":" << si1 << std::endl;
            std::cout << &si2 << ":" << si2 << std::endl;
            std::cout << &si3 << ":" << si3 << std::endl;
        }
        ~Test() {
            delete si1;
            delete si2;
            delete si3;
        }
};

int* Test::si1 = new int(4);
int* Test::si2 = new int(5);
int* Test::si3 = new int(6);
int static* si4 = new int(7);
static int *pcount = &count;
int main() {
    Test test;
    test.print();
    std::cout << &si4 << ":" << si4 << std::endl;
    delete si4;
    return 0;
}

输出结果展示:

//第一次的输出结果
01006770:010F5E70
0100676C:010F5EA0
01006774:010F5ED0
01006768:010F5CD8
//第二次的输出结果
01006770:00E05E70
0100676C:00E05EA0
01006774:00E05ED0
01006768:00E119C0

通过分析输出结果可以得知:变量si1、si2、si3、si4本身的地址是连续的(68-6C-70-74),所以说这4个指针变量是存放在静态区的,换句话说,关键字static只对这4个指针变量起作用;注意!!!static是只对你定义的变量起作用,尽管你这个变量是一个指针变量(指针变量只是表明你你定义的这个变量是存放地址的,可以进行解引用)。
第二个方面就是对new的理解,从输出结果可以看到,si1、si2、si3、si4保存的对象的地址不是连续的(CD8-E70-EA0-ED0),由此可以得知,通过new创建的对象实际上是在堆区上创建的,返回的地址也就是堆区内存的地址。换句话说,定义在静态存储区的指针变量是不需要你删除的(并且你也无法回收定义在程序静态存储区的指针变量),但是,请注意!!!虽然指针变量是放在静态存储区,但是你的指针变量保存的对象的地址却是通过new的方式在堆区创建的地址,你需要使用关键字delete来回收这块内存。
总而言之,static不具有传递性,staitc关键字只作用于当前定义的变量本身(换句话说,即使你定义的指针变量是存储在静态区的,但这并不意味着该指针指向的对象也是存储在静态区的,也可以是堆区的),new的数量必须和delete的数量完全一致,不然就会出现内存泄漏!!!

最后修改:2025 年 11 月 12 日
如果觉得我的文章对你有用,请随意赞赏