Подтвердить что ты не робот

Как распространять друга для производных классов

Я хочу иметь иерархию классов и иметь возможность создавать объекты из нее только внутри Factory.

Пример:

class Base
{
    protected:
        Base(){};
        virtual void Init(){};

    friend class Factory;
};

class SomeClass : public Base
{
    public://I want protected here! Now it possible to call new SomeClass from anywhere!
        SomeClass(){};
        void Init(){};
};

class Factory
{
    public:
        template<class T>
        T* Get()
        {
            T* obj = new T();
            obj->Init();

            return obj;
        }
};

int main()
{
    Factory factory;
    SomeClass *obj = factory.Get<SomeClass>();
}

Моя проблема в том, что я хочу иметь возможность создавать объекты только из Factory, но я не хочу объявлять friend class Factory в каждом классе, производном от Base.

Есть ли способ распространять друга в производных классах? Есть ли другой способ добиться такого поведения?

4b9b3361

Ответ 1

Нет, это преднамеренно невозможно.

Является ли проблема инкапсуляцией.

Предположим, что у вас есть класс "PswClass", который управляет любым паролем, который является каскадным другом с другим классом: если я наследую PswClass:

 class Myclass : public PswClass {
     .......
 }

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

Ответ 2

Дружба не наследуется и не транзитивна, как описано здесь: класс друга с наследованием.

После небольшого эксперимента и использования этого хака Как настроить глобальный контейнер (С++ 03)?, я думаю, что нашел способ предоставить уникальные права "factory" для создания объектов.

Вот быстрый и грязный код. (Прокрутите вниз, чтобы увидеть хак.)

class Object {};

class Factory {
public:
    // factory is a singleton
    // make the constructor, copy constructor and assignment operator private.
    static Factory* Instance() {
        static Factory instance;
        return &instance;
    }

    public: typedef Object* (*CreateObjectCallback)(); 
    private: typedef std::map<int, CreateObjectCallback> CallbackMap; 

public: 
    // Derived classes should use this to register their "create" methods.
    // returns false if registration fails
    bool RegisterObject(int Id, CreateObjectCallback CreateFn) {
        return callbacks_.insert(CallbackMap::value_type(Id, createFn)).second; 
    }

    // as name suggests, creates object of the given Id type
    Object* CreateObject(int Id) {
        CallbackMap::const_iterator i = callbacks_.find(Id); 
        if (i == callbacks_.end()) { 
            throw std::exception(); 
        } 
        // Invoke the creation function 
        return (i->second)(); 
    }

    private: CallbackMap callbacks_; 
};

class Foo : public Object {
    private: Foo() { cout << "foo" << endl; }
    private: static Object* CreateFoo() { return new Foo(); }


public:
    static void RegisterFoo() {
        Factory::Instance()->RegisterObject(0, Foo::CreateFoo);     
    }
};

class Bar : public Object {
    private: Bar() { cout << "bar" << endl; }
    private: static Object* CreateBar() { return new Bar(); }

public:
    static void RegisterBar() {
        Factory::Instance()->RegisterObject(1, Bar::CreateBar);     
    }
};

// use the comma operator hack to register the create methods
int foodummy = (Foo::RegisterFoo(), 0);
int bardummy = (Bar::RegisterBar(), 0);

int main() {
    Factory::Instance()->CreateObject(0); // create foo object
    Factory::Instance()->CreateObject(1); // create bar object
}

Ответ 3

Нет, нет способа наследовать объявление friend из базового класса. Однако, если вы создаете конструктор Base private, экземпляры производных классов не будут созданы без помощи Factory.

Ответ 4

Как уже говорили другие, дружба не наследуется.

это выглядит как хороший кандидат шаблона "Абстрактный Factory".

предполагается, что "SomeClass", полученные из базы, используются полиморфно. объявите абстрактную базу factory, которая создает базовые объекты. вывести каждый конкретный factory из базы, переопределить метод создания базы...

см. http://en.wikipedia.org/wiki/Abstract_factory_pattern для примеров

Ответ 6

В будущем, другая идея, появившаяся из chat между OP и мной, которая работает только с одним использованием friend, Конечно, это не универсальное решение, но оно может быть полезно в некоторых случаях.

Ниже приведен минимальный код, который показывает основные идеи. Это необходимо "интегрировать" в остальную часть кода Factory.

class Factory;

class Top { // dummy class accessible only to Factory
private:
    Top() {}
friend class Factory;
};

class Base {
public:    
    // force all derived classes to accept a Top* during construction
    Base(Top* top) {}  
};

class One : public Base {
public:    
    One(Top* top) : Base(top) {}
};

class Factory {
    Factory() {
        Top top;  // only Factory can create a Top object
        One one(&top);  // the same pointer could be reused for other objects
    }
};