还得是GPT-4o真强呀:单例模式在libsrt中的运用

比较好奇libsrt库的socket通讯如何实现的,就随意翻了翻socket通讯部分的代码,发现了一些技巧,特此记录。

libsrt库主要由C++开发的,看:

个别的是.c文件。
在这里插入图片描述

libsrt库的examples中的test-c-client.c和test-c-server.c共同组成了一个简单的srt客户端和server端:
在这里插入图片描述

只要是写过socket的,这些再简单不过了。

追踪:
在这里插入图片描述

继续追:

发现通讯部分都是调用的uglobal().xxxx();
在这里插入图片描述

没看uglobal()函数之前,疯狂的猜测:
1、难道是x实现的?
2、难道是xx实现的?
3、难道是xxx实现的?

看了代码才知道,原来用到了单例模式:

在这里插入图片描述

言归正传:

如果是Java的话,最常见单例模式的如下所写:

public class SingleTon {
	private static final SingleTon instance = new SingleTon();
	private SingleTon() {
	}
	public static SingleTon getInstance(){
		return instance;
	}
}

libsrt中C++的单例这样写的:


CUDTUnited& srt::CUDT::uglobal()
{
    static CUDTUnited instance;
    return instance;
}

这段代码就是实现了一个超级简单的单例,还运用了一个C++的特性:函数内的静态局部变量。

这段代码主要是把这个特性给整明白就可以了,我还有GPT-4o都觉得有如下好处:

  • 1、延迟初始化:局部静态变量只在函数首次被调用时初始化。这意味着如果从未调用该函数,实例也不会被创建。这在某些情况下可以节省资源和时间,因为不需要在程序开始时就创建单例实例。
  • 2、线程安全:从C++11开始,C++标准保证局部静态变量的初始化是线程安全的。也就是说,即使在多线程环境下,也只有一个线程能够初始化该变量,其他线程将等待初始化完成。这确保了单例模式在多线程环境中的安全性。
  • 3、控制实例化时机:因为实例是在函数第一次调用时创建的,所以程序员可以更精确地控制实例化的时机。这对一些需要依赖其他组件初始化的单例实例尤其重要。
  • 4、简化代码和封装:将静态变量声明在函数内部,可以将实例的创建和访问逻辑封装在一个地方,简化代码结构,使代码更加模块化和易于维护。

GPT-4o

请GPT-4o老师生成一个示例,展示为什么将静态变量写在函数内部更有优势:

#include <iostream>
#include <mutex>

// Singleton class example
class CUDTUnited {
public:
    // Public method to access the singleton instance
    static CUDTUnited& getInstance() {
        static CUDTUnited instance; // Local static instance
        return instance;
    }

    void connect(int u, const char* name, int namelen, bool forced_isn) {
        std::cout << "Connecting: " << name << std::endl;
    }

private:
    // Private constructor to prevent instantiation
    CUDTUnited() { std::cout << "CUDTUnited instance created." << std::endl; }

    // Delete copy constructor and assignment operator
    CUDTUnited(const CUDTUnited&) = delete;
    CUDTUnited& operator=(const CUDTUnited&) = delete;
};

// Usage example
void exampleUsage() {
    CUDTUnited& instance = CUDTUnited::getInstance();
    instance.connect(1, "example", 7, true);
}

int main() {
    exampleUsage();
    exampleUsage(); // The instance will not be created again
    return 0;
}

在上述代码中:

首次调用 getInstance 时,会输出 “CUDTUnited instance created.” 和 “Connecting: example”。
再次调用 exampleUsage 时,只会输出 “Connecting: example”,因为实例只会被创建一次。

对比:"函数外部静态变量"如果将静态变量写在函数外部,无法享受上述所有的好处,例如:

#include <iostream>

// Singleton instance declared outside of the function
static CUDTUnited instance; // Will be created at program startup

class CUDTUnited {
public:
    static CUDTUnited& getInstance() {
        return instance;
    }

    void connect(int u, const char* name, int namelen, bool forced_isn) {
        std::cout << "Connecting: " << name << std::endl;
    }

private:
    CUDTUnited() { std::cout << "CUDTUnited instance created." << std::endl; }
    CUDTUnited(const CUDTUnited&) = delete;
    CUDTUnited& operator=(const CUDTUnited&) = delete;
};

void exampleUsage() {
    CUDTUnited& instance = CUDTUnited::getInstance();
    instance.connect(1, "example", 7, true);
}

int main() {
    exampleUsage();
    exampleUsage(); // The instance will not be created again
    return 0;
}

在这种情况下:

程序启动时,无论是否使用 CUDTUnited 实例,都会创建实例。这可能会浪费资源。
线程安全问题:需要手动保证多线程环境下的线程安全,增加代码复杂性。

因此,将静态变量写在函数内部是单例模式实现的一种推荐方法,特别是从C++11开始,它提供了更好的延迟初始化和线程安全支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

手撕代码八百里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值