XSLT存档  

不及格的程序员-八神

 查看分类:  ASP.NET XML/XSLT JavaScripT   我的MSN空间Blog

C#

public sealed class Singleton  
{  
    private static volatile Singleton _instance;  
    private static readonly object LockObject = new object();  
  
    // 私有构造函数,防止外部实例化  
    private Singleton()  
    {  
        // 初始化代码  
    }  
  
    // 公共静态方法,用于获取实例  
    public static Singleton Instance  
    {  
        get  
        {  
            if (_instance == null)  
            {  
                lock (LockObject)  
                {  
                    if (_instance == null)  
                    {  
                        _instance = new Singleton();  
                    }  
                }  
            }  
  
            return _instance;  
        }  
    }  
  
    // 其他方法...  
}
public sealed class Singleton  
{  
    private static readonly Lazy<Singleton> LazyInstance = new Lazy<Singleton>(true);  
  
    // 私有构造函数,防止外部实例化  
    private Singleton()  
    {  
        // 初始化代码  
    }  
  
    // 公共静态属性,用于获取实例  
    public static Singleton Instance => LazyInstance.Value;  
  
    // 其他方法...  
}

 


 

Creating a Singleton Instance

Some classes of the Foundation and AppKit frameworks create singleton objects. In a strict implementation, a singleton is the sole allowable instance of a class in the current process. But you can also have a more flexible singleton implementation in which a factory method always returns the same instance, but you can allocate and initialize additional instances.The NSFileManager class fits this latter pattern, whereas theUIApplication fits the former. When you ask for an instance of UIApplication, it passes you a reference to the sole instance, allocating and initializing it if it doesn’t yet exist.

A singleton object acts as a kind of control center, directing or coordinating the services of the class. Your class should generate a singleton instance rather than multiple instances when there is conceptually only one instance (as with, for example, NSWorkspace). You use singleton instances rather than factory methods or functions when it is conceivable that there might be multiple instances someday.

To create a singleton as the sole allowable instance of a class in the current process, you need to have an implementation similar to Listing 2-15. This code does the following:

  • It declares a static instance of your singleton object and initializes it to nil.

  • In your class factory method for the class (named something like “sharedInstance” or “sharedManager”), it generates an instance of the class but only if the static instance is nil.

  • It overrides the allocWithZone: method to ensure that another instance is not allocated if someone tries to allocate and initialize an instance of your class directly instead of using the class factory method. Instead, it just returns the shared object.

  • It implements the base protocol methods copyWithZone:releaseretainretainCount, and autorelease to do the appropriate things to ensure singleton status. (The last four of these methods apply to memory-managed code, not to garbage-collected code.)

Listing 2-15  Strict implementation of a singleton

 1 static MyGizmoClass *sharedGizmoManager = nil;
 2  
 3 + (MyGizmoClass*)sharedManager
 4 {
 5     if (sharedGizmoManager == nil) {
 6         sharedGizmoManager = [[super allocWithZone:NULL] init];
 7     }
 8     return sharedGizmoManager;
 9 }
10  
11 + (id)allocWithZone:(NSZone *)zone
12 {
13     return [[self sharedManager] retain];
14 }
15  
16 - (id)copyWithZone:(NSZone *)zone
17 {
18     return self;
19 }
20  
21 - (id)retain
22 {
23     return self;
24 }
25  
26 - (NSUInteger)retainCount
27 {
28     return NSUIntegerMax;  //denotes an object that cannot be released
29 }
30  
31 - (void)release
32 {
33     //do nothing
34 }
35  
36 - (id)autorelease
37 {
38     return self;
39 }

If you want a singleton instance (created and controlled by the class factory method) but also have the ability to create other instances as needed through allocation and initialization, do not overrideallocWithZone: and the other methods following it as shown in Listing 2-15.


 

【原】ios下比较完美的单例模式,已验证

 

网上关于ios单例模式实现的帖子已经很多了,有很多版本,里面有对的也有不对的。我在使用过程中很难找到一个比较完美的方法,索性自己写一个吧,经过项目验证是比较合理的一个版本。

复制代码
static PRAutoLoginView *s_sharedInstance = nil;
+ (PRAutoLoginView *)shareInstance
{
    @synchronized(self)
    {
        if (s_sharedInstance == nil) {
            s_sharedInstance = [[[self class] hideAlloc] init];
        }
    }
    return s_sharedInstance;
}

#pragma mark --
#pragma mark singleton apis
+ (id)hideAlloc
{
    return [super alloc];
}

+ (id)alloc//彻底屏蔽掉alloc函数
{
    NSAssert(1 == 0, @"[PRAutoLoginView]please use +shareInstance instead of alloc!");
    return nil;
}

+ (id)new
{
    return [self alloc];
}

+ (id)allocWithZone:(struct _NSZone *)zone
{
    @synchronized(self)
    {
        if (s_sharedInstance == nil) {
            s_sharedInstance = [super allocWithZone:zone];
            return s_sharedInstance;
        }
    }
    return nil;
}

- (id)copyWithZone:(NSZone *)zone
{
    NSAssert(1 == 0, @"[PRAutoLoginView]copy is not permitted!");
    [self retain];
    return self;
}

- (id)mutableCopyWithZone:(NSZone *)zone
{
    return [self copyWithZone:zone];
}
复制代码

这里要注意的一点时,allocWithZone时默认调用的,即使你没有显式地调用alloc或者allocWithZone,因此需要重载


作者:Xi Yang

链接:https://www.zhihu.com/question/38101493/answer/75255810
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

来,骚年,给你个实例,我刚被坑过的:

template <typename T> class Singleton
{
public:
    static T& getInstance()
    {
        if (!instance)
            instance = new T();
        return *instance;
    }

private:
    static T* instance = nullptr;
};

这样就已经挂了。都不用考虑多线程的问题,还轮不到那个大坑发威。

如果你在main函数之前调用getInstance(),比如,一个全局对象的初始化函数里,那么由于main之前的初始化执行顺序不定,有可能实际上这样执行:

  • 调用getInstance(),instance被设为一个实例的地址;
  • instance静态变量被初始化为nullptr;
  • 后续的getInstance调用,又产生了一个实例。

于是,你不但 拥有两个不同的T实例,还八成泄漏了前一个。

这里还没考虑全局对象析构顺序不定造成的问题。

=================

再补充一条:编译器实现不一致。

  • VS2013没有noexcept。throw()并不能完全代替noexcept。
  • VS2013没有alignas,而微软内置的alignment声明机制不支持用alignof的结果作为一个参数,你只能直接给常量。
  • MMX指令集似乎已经嗝屁了,但64位GCC仍然在提供MMX的intrinsics。我已经弄不清楚这是应该还是不应该了。
  • Clang缺很多intrinsics,比如fxsave。
编辑于 2022-09-13 14:29・IP 属地北京
 
不是归人
template <typename T> class Singleton
{
public:
static T& getInstance()
{
static T instance;
return instance;
}
};
2015-12-04
知乎用户ihF64A
梅耶法,使用C++ 11才能确保ok
2017-07-25
费庄轩
看过Effective C++的就是不一样
2015-12-07
farta
各种C++守则建议说过多少遍,全局对象的初始化不要互相依赖,就是记不住。。。
2016-01-18
 
IQTester
不要使用全局静态变量,除非是整数等简单类型,就是因为静态变量初始化顺序不确定。
2015-12-05
Xi Yang
作者
简单类型好像也不定。。。
2015-12-05
IQTester
的确是,忽略了。我其实一般都是用上面人们提到的只在函数内部使用静态变量,其实就是弄个函数取代直接使用变量。
2015-12-05
梅启铭(vczh)
 
我关注的人
我自己写的singleton,除了只在你需要的时候构造以外,都会提供给你一个删除函数,你自己需要在main函数结束的时候显式调用。全局变量不会留下任何构造函数和析构函数。如果你没有析构,那就会在结束的时候被判定为内存泄露。简单粗暴。

c++11如何实现单例模式?

 
网上百度到的方法都不能通过编译,后来度娘告诉我说类静态成员变量必须在类外初始化。。。好蛋疼
还请大大从简单到复杂的多线程、析构函数等等介绍一下c++11的单件模式
被浏览
193,271
 
 
 

单例version1

单例是一个很好理解的概念,即保证类在全局只有一个实例,并提供一个该实例的全局访问点。

代码实现起来思路也很简单,我们先来写个最简单的版本

class Singleton {
public:
    static Singleton * GetInstance() {
        return instance;
    }
private:
    Singleton(){}//构造
    Singleton(const Singleton &clone){} //拷⻉构造
    Singleton& operator=(const Singleton&) {}
    static Singleton * instance;
};
Singleton* Singleton::instance = new Singleton();//静态成员需要初始化

我们怎么才能实现全局只有一个实例呢?那就是让用户无法自己定义一个Singleton对象。

将构造函数,拷贝构造,拷贝赋值运算符都设为私有成员,这样外界便无法通过构造函数创建新的实例。

之后便是给外界提供一个接口,方便外界获取全局唯一的一个实例。

我们可以用静态变量实现,设置一个singleton指针instance方便未来存储唯一singleton实例。

这里还有一个知识点,无论是静态成员还是静态方法,其存储位置都和singleton类割裂开的。

虽然静态变量声明在类内部,但是存储空间不一样,静态成员会在编译阶段存在静态区,如果有初始化,会放在data段,如果无初始化,会放在bss段。

像a,b这种普通成员,会根据定义类的位置,存储在栈空间或者堆上。

既然静态成员的存储位置和普通成员分开,自然普通成员方法无法“看到”类的静态成员,我们提供给外界的访问api也必须用静态成员方法,也就是上述的GetInstance()。

最后,c++还有个比较容易遗忘的规则,静态成员的初始化必须放在类外。(也就是代码最后一行)

饿汉/懒汉模式

单例的实现有饿汉,懒汉之分。

version1的代码实现的是饿汉模式,即在单例定义的时候(main函数之前)就行实例化,这样的好处是线程安全,在多线程的环境下无论哪个线程通过GetInstance()获得的都是同一个实例(同一个地址)

坏处是浪费了额外的存储空间,若我们导入一个库,其中定义了饿汉模型的单例类,那么即便我们后面不用这个单例,其依旧会实例化,占用空间。

那么我们当然希望这个单例类在整个程序中第一次用到的时候才实例化,不用不实例化,这种模式就是懒汉模式,我们将version1的代码浅修改一下。

class Singleton {
public:
    static Singleton * GetInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
private:
    Singleton(){}//构造
    Singleton(const Singleton &clone){} //拷⻉构造
    Singleton& operator=(const Singleton&) {}
    static Singleton * instance;
}
Singleton* Singleton::instance = nullptr;//静态成员需要初始化

我们完成了一个最简单的懒汉模式。instance指针初始化为nullptr

当我们第一次调用GetInstance()方法,此时发现instance指针是空指针,就会new一个Singleton对象并返回。

单例version2

懒汉模式的单例有个极大的问题。它是线程不安全的,假如多个线程同时调用GetInstance()发现instance指针是空指针,于是都开始new一个Singleton,这就会造成单例的崩坏,而且容易导致内存泄漏

同时new并不是一个原子操作,哪怕在高层语言,都非原子,new操作包括了三个步骤:

  • void* ptr = malloc(sizeof(Singleton)); //-分配空间
  • instance = (Singleton*)ptr; //-赋予指针步长
  • Singleton() //-执行构造函数

所以一个线程在任何一个过程都有可能被其它线程打断。会造成各种问题。

下面代码利用了双重检测+互斥锁+atomic变量+ c++11内存屏障实现了线程安全懒汉单例。

如果你对c++11的atomic和内存屏障有所疑惑,可以看我写的这篇前导文章,希望有所帮助。

玉米:多线程(一):C++11 atomic和内存序

#include <mutex>
#include <atomic>
class singleton{
private:
    static std::atomic<singleton*> instance_;//-静态实例指针
    static std::mutex mutex_;//-互斥锁
    static void Destroy(){
        singleton * temp = instance_.load(std::memory_order_relaxed);
        if(temp!=nullptr){
            delete temp;
        }
    }
    singleton(){}
public:
    singleton(const singleton &)=delete;
    singleton & operator = () = delete;
    singleton(singleton &&) = delete;
    singleton & operator = (singleton && ) = delete;
    static singleton * getInstance(){
        //-宽松序获取当前instance(无视顺序重排,即便该语句被重排到fence之后也没关系,因为最多会被重排到if语句之前)
        singleton * temp = instance_.load(std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_acquire);//-设置acquire内存屏障,意味着以下读写不会被重排到acquire之前
        if(temp == nullptr){
            //-等待拿到互斥锁
            std::lock_guard <std::mutex> lock(mutex);
            //-需要再次判断其它线程是否已经修改了instance
            temp = instance_.load(std::memory_order_relaxed);
            if(temp == nullptr){
                //-new一个新的instance
                temp = new singleton;
                //-解除内存屏障
                std::atomic_thread_fence(std::memory_order_release);//-当前线程读写不会被重排到release之后
                //-将新的值写入到instance_里
                instance_.store(temp,std::memory_order_relaxed);
                //-注册析构函数
                atexit(Destroy);
            }
        }
        return temp;
    }
};
//-静态成员需要在类外初始化
singleton::instance_;
singleton::mutex_;

我们来逐个分析这个代码用到的机制。

  • atomic类的instance:

将instance定义成c++11的atomic类型变量,并使用load和store接口进行instance的读和写,就能保证读和写都是原子操作,即在多线程环境下读写是可靠的,不会在过程中被其它线程给打断。

  • 互斥锁:

当我们要new第一个实例并赋值给instance的时候,我们自然希望这整个过程是只有唯一一个线程在进行的,同时整个过程并不方便做成一个原子操作,所以在这里使用互斥锁。当我们获取到instance为nullptr的时候加锁,当我们完成实例化后解锁。

  • 双重检测:

双重检测为了弥补互斥锁的缺陷:也就是某个线程如果拿不到锁,就会阻塞。直到其它线程释放了锁,会重新恢复执行。

想象一个场景,线程t1第一个拿到锁,并给instance实例化,而其它线程t2,t3拿不到锁,陷入阻塞,当t1实例化完,t2拿到锁,此时t2会从获取锁代码处继续往下执行,继续给instance实例化,然而此时instance已经被t1实例化了,多次实例化自然有问题,所以在获取锁以后,需要再次判断instance是否为nullptr,这就是所谓的双重检测。

  • 内存屏障:为了保证双重检测的正确执行,我们必须避免双重检测代码在cpu处被重排,在前导文章中我讲了内存序来控制cpu重排,memory_order_acquire保证了该屏障后的读写代码不会被重排到acquire屏障之前,memory_order_release屏障保证了该屏障前的读写代码不会被重排到release屏障之后。也就意味着在acquire和release之前的代码“结冰”了,会非常严格按顺序执行。

单例 version3

上面的代码虽然实现了线程安全懒汉单例,但是用的机制太多也很麻烦。那么有没有更简单的方法呢?

答案是还真有,我们可以利用c++11的静态局部变量来实现线程安全懒汉单例,

代码如下:

class singleton{
private:
    singleton();//-将构造函数设为私有,这样用户无法自己创建对象
public:
    //-将拷贝构造,移动构造,拷贝运算符,移动运算符删除
    singleton(const singleton&) = delete;
    singleton& operator=(const singleton&) = delete;
    singleton(singleton &&) = delete;
    singleton & operator = (singleton && ) = delete;
    //-public析构,当进程结束后,内存销毁静态局部变量时会调用析构
    ~singleton();

    //-只适合c++11以上标准
    static singleton& getInstance(){
        //-静态局部变量实现懒汉模式,只有第一次调用getInstance的时候才会初始化instance,且全局只会初始化一次
        static singleton instance;
        return instance;
    }
};

首先,静态局部变量通过静态方法获得,且只有当该静态方法第一次执行的时候才会初始化,且后续再次调用该方法也不会再次初始化,这是静态局部变量的特性。

而在c++11以上标准中,静态局部变量初始化是线程安全的。

当其中一个线程初始化instance的时候,会阻塞其它线程的初始化行为。

发布于 2022-07-11 16:02・IP 属地陕西
 
 

更多回答

 
 
class Singleton {
public:
	static Singleton& getInstance(){
		static Singleton instance;
		return instance;
	}
private:
	Singleton ()= default;
	~Singleton ()= default;
	Singleton (const Singleton &)= delete;
	Singleton & operator=(const Singleton &)= delete;
};

c++11标准做法.

评论千万条,友善第一条
 
 
36 条评论
 
默认
最新
chaya
 

没错 c++11 局部静态变量已经是线性安全了

2017-06-14
你法我笑
请教一下,如果是局部静态指针对象呢?也是线性安全吗?
2022-07-24
唯心所现

缺一句:private: Singleton(){}

2018-02-08
杰子

还有拷贝赋值、移动构造、移动赋值通通=delete

2020-08-13
诸葛不亮
还有拷贝构造=delete
2020-07-22
bug拌饭
你这个是高赞,但是并不完整。建议补全。
private:
Singleton ()= default;
~Singleton ()= default;
Singleton (const Singleton &)= delete;
Singleton & operator=(const Singleton &)= delete;
2022-01-29
桃毛爸爸

《Best of All》在跨DLL的时候,不同DLL内各有一份实例耶。严格意义上上,这个不算单例啊。

2020-12-02
C加加辅导老师
一批有业务上下游关系的单例类型如何保证单例对象创建的顺序?
2019-03-09
Alives
 
作者
弄一个init统一创建。
2019-06-13
Esicer
 

编译都通不过

2018-05-13
Alives
 
作者

什么编译器

2018-05-13
bluepill
误导人啊
2022-08-20
franklinyu
04-03
 
 
无法实现延迟初始化
2022-11-26
GHome
 
是延迟的
04-02
洛江月
这个是放在头文件里面吗?
头文件里面的static 函数会不会被隐式内联,实际上每个每个包含了这个头文件的地方都有一份实现?
2019-12-03
洛江月
我又仔细查了一下,内联不会导致 静态局部变量 有多份
这样写是安全的
2019-12-03
 
但是如果是在动态库里就会有两份了,所以最好还是不要在头文件里写
2022-01-29
好似南柯一梦
头像
2019-04-09
Alives
 
作者
头像咋了
2019-06-13
sjunter
 

Singleton s = Singleton::getInstance() 会执行拷贝初始化,这样不就产生两个实例了吗?

2018-03-29
会飞天的喵

虽然时间有点久了,但还是回一下:Singleton &s = Singleton::getInstance() 可以获取引用;析构函数就正常写要做什么就好了(比如清理资源,关闭文件等)

2022-07-22
sjunter

意思是不是把s声明成引用类型就可以了?还有类的析构函数又该如何定义呢?

2018-03-30
炼金术士

函数前面的static是不是不一定需要?这个static只是用于内部链接吗

2020-12-14
Alives
 
作者
我这个是成员函数。
2020-12-14
炼金术士

如果是类的成员函数的话才要前面加static吧?

全局函数加不加static不是对外可不可见的区别吗?

void test() {
static int a = 0;
a++;
cout << a << endl;
}

int main() {
test();
test();
test();
}

这段代码可以正常使用

2020-12-14
 
 
很遗憾,vs2013不安全,坑死了
2020-10-16
Alives
 
作者

vs2015才支持,

2020-10-16
小明
 
析构时可能存在问题,无法决定析构顺序。。比如,LogSingleton(日志),ScreenSingletion(屏幕),如果ScreenSingletion析构异常,需要通过LogSingleton写LOG,但此时LogSingleton可能已经析构,这样程序就访问未构造的内存,导致未定义异常。
2022-01-29
萧涵
 
单例不保证顺序,顺序靠程序员自己保证,例如统一的Init()函数和release()函数。
2022-07-11
 
 
评论千万条,友善第一条
 
 
 
 
 

如果你只是需要全局访问,把一堆函数跟变量直接放进某个新 namespace 中就行。连 class 都不需要定义。

namespace 就是C++语言级天生的单例。你有没有想过单例根本不需要是 class?

之所以有单例模式这种东西,本质上是因为Java没有全局变量。

都写C++了,就不必坚持用class来制造单例了。

除非你需要使用的是某个系统级,无法重复获取的东西。


补充:如果需要控制某个变量的初始化时机,怎么办?答案是把它作为这个namespace的私有变量,自行控制其初始化进程。然后增加相关函数用于主动触发相关初始化。

 
posted on 2013-01-29 11:05  不及格的程序员-八神  阅读(273)  评论(0编辑  收藏  举报