не глобальный шаблон проектирования Singleton?

Возможный дубликат:
Singleton, который не глобально доступны

Знаете ли вы хороший шаблон проектирования, который гарантирует, что будет создан только один экземпляр объекта, не делая этот объект глобальным в C++? Это то, что делает синглтон, но мне действительно нужно, чтобы он НЕ был глобальным по соображениям безопасности доступа к коду.

Спасибо за вашу помощь!


person koleror    schedule 23.12.2011    source источник
comment
Типичный шаблон singleton инкапсулирует объект singleton как закрытый статический член класса singleton и предоставляет метод Instance() для доступа. Разве этого недостаточно?   -  person Dave Rager    schedule 24.12.2011
comment
Вы хотите, чтобы только некоторые классы могли извлекать одноэлементный объект?   -  person davogotland    schedule 24.12.2011
comment
Нет, мне не разрешено использовать Singleton, потому что с этим шаблоном экземпляр объекта является глобальным, а затем доступен везде в программе, что не является безопасным.   -  person koleror    schedule 24.12.2011
comment
он не будет доступен из любой точки программы, если он инкапсулирован и приватен внутри класса, верно? конечно, любой может получить его, но это можно исправить с помощью взлома друга, упомянутого в некоторых ответах.   -  person davogotland    schedule 24.12.2011
comment
@davogotland: да, если у вас есть хороший способ, позволяющий только одному объекту создавать экземпляр объекта, я думаю, было бы здорово! Я думал о фабрике, которая даст свой экземпляр в параметре конструктора объекта, но проблема в том, что она не является общей, и мы не можем запретить пользователю создавать экземпляр другой фабрики...   -  person koleror    schedule 24.12.2011
comment
@user455998 user455998 извини, но ты нескладно. это как если бы вы не тестировали шаблон singleton. как вы имеете в виду, что разрешен только один объект?   -  person davogotland    schedule 24.12.2011
comment
Кроме того, singleton не делает объект глобальным, он делает объект доступным глобально. это очень большая разница.   -  person davogotland    schedule 24.12.2011
comment
@davogotland: я не вижу разницы, кроме формулировки.   -  person GManNickG    schedule 24.12.2011
comment
разница в том, что если бы указатель объекта был глобальным, то любой мог бы изменить объект, на который указывает этот указатель. так что тогда не было бы никакой гарантии, что каждый всегда получит один и тот же объект. но поскольку указатель скрыт внутри класса, доступ к фактическому указателю запрещен, и предоставляется доступ только к объекту. теперь любой может изменить содержимое объекта, но объект всегда останется прежним. это как дед мороз. Санта-Клаус из года в год приносит разные вещи, и каждый имеет доступ к изменению того, что он приносит. но его никто не заменит ;)   -  person davogotland    schedule 24.12.2011
comment
@davogotland: Откуда взялся указатель объекта? Мы говорили о том, что объект является глобальным, а не глобально доступным. Я ничего не вижу там про указатели.   -  person GManNickG    schedule 24.12.2011
comment
@GMan хорошо ... это как бы подразумевалось, поскольку вы начали говорить о шаблоне singleton, который обычно реализуется с использованием указателей.   -  person davogotland    schedule 24.12.2011
comment
@davogotland: Не совсем так. Даже если бы было совершенно очевидно, что мы говорим об указателях, использование слова «объект» означало бы просто объект, на который указывают. Но это обсуждение спорно, поскольку мы просто спорим о семантике.   -  person GManNickG    schedule 24.12.2011
comment
как насчет этого: что, если бы вы могли сделать так, чтобы 1) мог быть только один экземпляр объекта в каждый момент времени, 2) было одно место в программе, где статический указатель удерживает этот единственный объект, даже если никто другой в настоящее время не указывает на этот объект, 3) и этот статический указатель не доступен никому, кроме методов внутри класса, экземпляром которого является один объект, 4) и, наконец, только объекты одного (или некоторые) класс(ы) могут получить этот один объект. удовлетворит ли это ваши потребности?   -  person davogotland    schedule 24.12.2011
comment
@Xeo: Вау, я забыл об этом вопросе и на этот раз написал свой ответ почти так же. Было странно читать мысли: «Вау, этот парень думал то же самое, что и я».   -  person GManNickG    schedule 24.12.2011
comment
@GMan: Да, но эта версия лучше. :) Исключение + локальная статика.   -  person Xeo    schedule 24.12.2011
comment
@Xeo: Да, хотя, судя по последнему комментарию к моему ответу в другом вопросе, это, вероятно, должно быть статическое значение для всего класса, чтобы его можно было установить обратно на false в деструкторе.   -  person GManNickG    schedule 24.12.2011
comment
@GMan: Хороший вопрос, инкапсуляция в статическую функцию должна решить эту проблему. static bool& created(){ static bool _created = false; return _created; }.   -  person Xeo    schedule 24.12.2011
comment
@Xeo: Ах, я только что снова сделал это для всего класса. :) Я полагаю, что это можно считать более чистым.   -  person GManNickG    schedule 24.12.2011


Ответы (4)


Я думаю, вы хотите что-то вроде этого (примечание: скопировано из ответа, который я уже написал и забыл о нем):

#include <stdexcept>

// inherit from this class (privately) to ensure only
// a single instance of the derived class is created
template <typename D> // CRTP (to give each instantiation its own flag)
class single_instance
{
protected: // protected constructors to ensure this is used as a mixin
    single_instance()
    {
        if (mConstructed)
            throw std::runtime_error("already created");

        mConstructed = true;
    }

    ~single_instance()
    {
        mConstructed = false;
    }

private:
    // private and not defined in order to
    // force the derived class be noncopyable
    single_instance(const single_instance&);
    single_instance& operator=(const single_instance&);

    static bool mConstructed;
};

template <typename T>
bool single_instance<T>::mConstructed = false;

Теперь вы получаете исключение, если класс создается более одного раза:

class my_class : private single_instance<my_class>
{
public:
    // usual interface (nonycopyable)
};

int main()
{
    my_class a; // okay
    my_class b; // exception
}

Однако в C++ нет способа применить политику одного экземпляра во время компиляции.


(Также хорошо, что вы заметили, что синглтоны глупы. Глобально доступный и отдельно создаваемый — это две разные концепции, и их следует комбинировать только по совпадению, а не по замыслу.)

person GManNickG    schedule 24.12.2011

Вы можете использовать синглтон с типичной статической функцией доступа Instance(), но сделайте эту функцию private. Затем доступ предоставляется только другому классу, который становится дружественными классами класса-одиночки.

person Johannes Gerer    schedule 24.12.2011
comment
Это ничем не отличается от глобального с дополнительным уровнем косвенности (какую ошибку вы получаете, когда этот дружественный класс создается в двух разных местах? Нет? Тогда вопрос остается невыполненным.) - person GManNickG; 24.12.2011
comment
я думаю, что Йоханнес Герер прав, потому что только члены друга могут получить доступ к instance(), поэтому доступ к услугам, предоставляемым синглтоном, и его данные зарезервированы для функций класса друга. интересно, может ли размещение синглтона внутри другого класса также решить проблему - person Hicham; 24.12.2011
comment
@eharvest: как только он станет одноэлементным, он станет глобальным навсегда. Обойти это невозможно (за исключением неправильной реализации синглтона). И если вы собираетесь попытаться обойти это, зачем вообще начинать с синглтона? - person GManNickG; 24.12.2011
comment
@GMan: 1. Singleton == Будет только один экземпляр этого класса (это не я придумал, вы должны это знать). 2. Запрошенная OP безопасность доступа к коду: (уникальный) одноэлементный объект будет доступен только для классов друзей. 3. Количество объектов класса друзей не ограничено (ни вопросом, ни моим ответом) 4. Никакого вмешательства. - person Johannes Gerer; 24.12.2011
comment
@JohannesGerer: Ваш № 1 неполный: Singleton == Существует единственный глобальный экземпляр класса (вы должны это знать). Он не хочет глобального. Он не хочет, чтобы несколько экземпляров класса работали с одним и тем же экземпляром внизу, он хочет, чтобы несколько экземпляров были ошибкой. И да, сокрытие глобального объекта за прокси-классом по определению является косвенным. - person GManNickG; 24.12.2011

Сделайте конструктор(ы) и оператор присваивания закрытыми.

Затем сделайте единственную функцию, которая создает один экземпляр вашего класса, другом класса.
Поскольку вы пишете функцию, только она может создать объект (и других в приложении не будет).

person Martin York    schedule 24.12.2011
comment
Я знаю, что могу легко контролировать создание объекта, но проблема в том, что однажды созданный объект является глобальным и доступен из всей программы. Я ошибаюсь? Если да, можете ли вы дать мне какой-нибудь псевдокод, который покажет мне, как это сделать? Спасибо! - person koleror; 24.12.2011
comment
одиночный экземпляр не является глобальным, если не является метод экземпляра класса синглтона или ссылка на него. созданный синглтон доступен только коду, который имеет ссылку на него или может получить ссылку на него. ссылка на синглтон может быть закрытой, а метод, описанный Йоханнесом и Локи, может гарантировать, что вы контролируете, какой код имеет доступ к методу класса/экземпляра; поэтому дает вам полный контроль. - person Richard Logwood; 24.12.2011

вы можете контролировать видимость вашего одноэлементного метода класса/экземпляра, используя пространство имен и/или вложенные и локальные классы

учебник по синглтону http://www.yolinux.com/TUTORIALS/C++Singleton.html

пример локального класса http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=191

person Richard Logwood    schedule 24.12.2011