class Singleton
{
private Singleton() {}
private static Singleton _instance;
public static Singleton Instance()
{
lock(typeof(Singleton))
{
if(_instance == null)
_instance = new Singleton();
}
return _instance;
}
}
Уникальный экземпляр класса
Вопрос 1:
Как написать класс такой, что будучи однажды созданым, при последующих статических определениях возвращалась ссылка на этот первый созданый объект? Вернее, можно ли такое провернуть?
Вопрос 2:
Для динамического создания такого объекта достаточно переопределить операторы new и delete класса.Или я в чем-то ошибаюсь?
Спасибо за внимание
Код:
или проще:
Код:
sealed class Singleton
{
private Singleton() {}
public static readonly Singleton Instance = new Singleton();
}
{
private Singleton() {}
public static readonly Singleton Instance = new Singleton();
}
на эту тему можно найти кучу примеров. поищи на "C# singleton".
Free Thinker, singleton это паттерн проектирования для ООП впринципе, а не для конкретно C# =)
спасибо, бум знать. просто я им только на C# пользовался.
Код:
// Файл myclass.h
class MyClass
{
public:
void *operator new (size_t);
void operator delete (void*);
void *operator new[] (size_t);
void operator delete[] (void*);
private:
static MyClass *singleton;
};
// Файл myclass.cpp
#include "myclass.h"
MyClass *MyClass::singleton = 0;
void *MyClass::operator new (size_t)
{
if (MyClass::singleton == 0)
singleton = ::new MyClass;
return singleton;
};
void MyClass::operator delete (void*)
{
if (singleton == 0) // Надо вызвать некое исключение (замените строку ниже на нужное исключение)
throw 1;
delete MyClass::singleton;
MyClass::singleton = 0;
};
void *MyClass::operator new[] (size_t)
{
// Замените строку на нужное исключение
throw 1;
};
void MyClass::operator delete[] (void*)
{
// Замените строку на нужное исключение
throw 1;
};
class MyClass
{
public:
void *operator new (size_t);
void operator delete (void*);
void *operator new[] (size_t);
void operator delete[] (void*);
private:
static MyClass *singleton;
};
// Файл myclass.cpp
#include "myclass.h"
MyClass *MyClass::singleton = 0;
void *MyClass::operator new (size_t)
{
if (MyClass::singleton == 0)
singleton = ::new MyClass;
return singleton;
};
void MyClass::operator delete (void*)
{
if (singleton == 0) // Надо вызвать некое исключение (замените строку ниже на нужное исключение)
throw 1;
delete MyClass::singleton;
MyClass::singleton = 0;
};
void *MyClass::operator new[] (size_t)
{
// Замените строку на нужное исключение
throw 1;
};
void MyClass::operator delete[] (void*)
{
// Замените строку на нужное исключение
throw 1;
};
Операторы new[] и delete[] перегружены только для того, чтобы пользователь ими не пользовался - в случае использования генерируется исключение.
Есть более "красивый" способ - декларировать эти операторы, но не реализовать, т. е. выкинуть соответствующий код из myclass.cpp.
Тогда попытка использования new[] и delete[] будет пресекаться еще во время компиляции.
По идее, нужно добавить счетчик ссылок, чтобы программа могла вызывать new многократно и столько же раз потом вызывать delete. Для этого, добавляем в класс статический член - счетчик:
Код:
class MyClass
{
...
static size_t singleton_count;
}
{
...
static size_t singleton_count;
}
И myclass.cpp должен выглядеть примерно так:
Код:
#include "myclass.h"
MyClass *MyClass::singleton = 0;
size_t MyClass::singleton_count = 0;
void *MyClass::operator new (size_t)
{
if (MyClass::singleton_count == 0)
singleton = ::new MyClass;
++MyClass::singleton_count;
return singleton;
};
void MyClass::operator delete (void*)
{
if (MyClass::singleton_count == 0)
throw 1;
if (--MyClass::singleton_count == 0)
delete MyClass::singleton;
MyClass::singleton = 0;
};
MyClass *MyClass::singleton = 0;
size_t MyClass::singleton_count = 0;
void *MyClass::operator new (size_t)
{
if (MyClass::singleton_count == 0)
singleton = ::new MyClass;
++MyClass::singleton_count;
return singleton;
};
void MyClass::operator delete (void*)
{
if (MyClass::singleton_count == 0)
throw 1;
if (--MyClass::singleton_count == 0)
delete MyClass::singleton;
MyClass::singleton = 0;
};
Другой способ - вообще запретить многократный вызов new.
Для этого следует запретить new и delete, конструктор и деструктор сделать приватными.
Код:
// Файл MyClass2.h
class MyClass2
{
public:
void *operator new (size_t); // Операторы объявляем, но не реализуем
void operator delete (void*);
void *operator new[] (size_t);
void operator delete[] (void*);
MyClass2 &get () { return singleton; };
private:
MyClass2(); // При желании добавить параметры
~MyClass2();
static MyClass2 singleton;
};
// Файл MyClass2.cpp
#include "myclass2.h"
MyClass2 MyClass2::singleton; // При желании добавить параметры (в скобках)
class MyClass2
{
public:
void *operator new (size_t); // Операторы объявляем, но не реализуем
void operator delete (void*);
void *operator new[] (size_t);
void operator delete[] (void*);
MyClass2 &get () { return singleton; };
private:
MyClass2(); // При желании добавить параметры
~MyClass2();
static MyClass2 singleton;
};
// Файл MyClass2.cpp
#include "myclass2.h"
MyClass2 MyClass2::singleton; // При желании добавить параметры (в скобках)
Отличие такого способа от перегрузки new/delete в том, что экземпляр класса создается сразу при старте программы и уничтожается при ее завершении. Если экземпляр класса должен, например, создаваться не ранее какого-то момента, а уничтожаться не позднее какого-то, такой способ не годится.
---
Рекомендую книгу:
Э. Гамма, Р. Хелм и др.
Приемы объектно-ориентированного проектирования. Паттерны проектирования.
Стр. 130. Паттерн Singleton
Достаточно просто сделать приватными конструктор/деструктор.
Кстати, в.т.ч. и конструктор копирования.
Зачем создавать экземпляр на старте?
Его вполне можно создать при первой необходимости, например, при первом к нему обращении.
Все значительно проще:
Код:
class Singleton
{
public:
static Singleton& getInstance() {
if(_instance == NULL) {
_instance = new Singleton;
}
return *_instance;
}
private:
Singleton();
Singleton(const Singleton&);
~Singleton();
static Singleton* _instance;
};
Singleton* Singleton::_instance = NULL;
{
public:
static Singleton& getInstance() {
if(_instance == NULL) {
_instance = new Singleton;
}
return *_instance;
}
private:
Singleton();
Singleton(const Singleton&);
~Singleton();
static Singleton* _instance;
};
Singleton* Singleton::_instance = NULL;
Действительно, как-то не подумал...
Если есть несколько потоков то функцию getInstance надо както синхронизировать.
Цитата: Rebbit
Если есть несколько потоков то функцию getInstance надо както синхронизировать.
Если есть несколько потоков, то придется синхронизировать далеко не только эту функцию. :)