Доступ к модификаторам переменной: Private vs Protected - PullRequest
0 голосов
/ 23 ноября 2010

У меня есть случай, когда я не смог найти правильного решения. Мне нужно иметь базовый класс шаблона и класс менеджера, который выполняет некоторую работу с этим базовым классом. Вот код

class Base;

class Manager
{
public:
    void DoSomethingTo(Base * bclass)
    {
        if(bclass->PrivateVar == 0)
            // Some code
    }
}

class Base
{
protected: 
    int PrivateVar;
friend class Manager;
};

template<class T>
class TempBase : public Base
{
private:
    Manager * man;
public:
    void DoWork()
    {
        PrivateVar = 0;
        man->DoSomethingTo(this);
    }
}

Моя проблема в том, что переменная PrivateVar на самом деле должна быть закрытой для TempBase и не быть доступной для классов, производных от TempBase, но, поскольку я не могу передать ее указатель на Manager::DoSomethingTo(), я должен извлечь ее из базового класса, а базовый класс PrivateVar. И поскольку я использую PrivateVar в TempBase, он не может быть закрытым для Base, что делает его доступным из дочерних классов TempBase.

Как мне написать класс TempBase, чтобы PrivateVar был закрытым членом, тогда как он может быть достигнут с Manager::DoSomethingTo()?

Спасибо

Ответы [ 5 ]

2 голосов
/ 23 ноября 2010

Прежде всего, давайте начнем войну пламени:

Атрибут класса никогда не имеет причины быть protected. Это должно быть либо private (в большинстве случаев), либо, возможно, в некоторых угловых случаях public. Обратите внимание, что, поскольку каждый производный класс будет видеть атрибут protected, protected приносит только ложное чувство безопасности: никто не может управлять любым инвариантом атрибута protected, поэтому по сути это public, с комментарием Не трогай поверх него.

Теперь, когда это сказано и сделано, я бы предложил просто изменить это на:

class Base {};
template <typename T> class TBase;

class DoKey { template <typename T> friend class TBase; DoKey(); };

class Manager
{
public:
  void DoSomethingTo(Base& base, DoKey const& key);
};

template <typename T>
class TBase: public Base
{
public:
  void Do()
  {
    Manager manager;
    if (PrivateVar == 0) { manager.DoSomething(*this, DoKey()); }
  }

private:
  int PrivateVar;
};

Передавая ответственность за проверку владельцу переменной, мы осторожно обходим все наши заботы. Конечно, если нам нужно, чтобы несколько классов вели себя одинаково, мы могли бы ввести чистый виртуальный void ShouldIDoSomething() const в Base и проверить его в методе Manager.

1 голос
/ 23 ноября 2010

Снимите защиту доступа внутри Base, затем наследуйте конфиденциально.

class Manager
{
public:
    void DoSomethingTo(struct Base * bclass);
};

struct Base
{
    int PrivateVar;
};

void Manager::DoSomethingTo(struct Base * bclass)
{
    if(bclass->PrivateVar == 0)
        ; // Some code
}

template<class T>
class TempBase : private Base
{
private:
    Manager * man;
public:
    void DoWork()
    {
        PrivateVar = 0;
        man->DoSomethingTo(this);
    }
};
0 голосов
/ 23 ноября 2010

Я подумал, что мог бы разместить в Base открытую виртуальную функцию для извлечения PrivateVar, сохраняя PrivateVar в TempBase.

class Manager
{
public:
    void DoSomethingTo(Base * bclass)
    {
        if(bclass->GetPrivateVar() == 0)
            // Some code
    }
}
class Base
{
private:
virtual int GetPrivateVar() = 0;  
friend class Manager;
};

template<class T>
class TempBase : public Base
{
private:
    Manager * man;
    int PrivateVar;
    int GetPrivateVar() {return PrivateVar;}
public:
    void DoWork()
    {
        PrivateVar = 0;
        // Some code
        man->DoSomethingTo(this);
        // Some code
    }
friend class Manager;
}
0 голосов
/ 23 ноября 2010

Вы можете сделать PrivateVar личным, и сделать TempBase и Manager друзьями Base. Смотрите здесь . Обратите внимание, что этот код не самый чистый.

0 голосов
/ 23 ноября 2010

Вы можете поместить защищенную функцию-член в саму базу:

class Base
{
protected:
   void sendPrivateVarToManager( Manager& manager )
   {
      manager.DoSomethingToPrivateVar( privateVar );
   }
};

Поскольку Base не является шаблоном, реализация может фактически быть помещена за пределы Base.

Другая ваша проблема в том, что вы хотите, чтобы privateVar существовал в Base, был видимым для TempBase, но не для чего-либо еще ниже по иерархии. Либо вы можете переместить его в TempBase, и в этом случае вы также можете поместить sendPrivateVarToManager в этот класс, хотя тогда вам придется реализовать его, либо передать privateVar в качестве параметра для этого. Другой вариант - сделать TempBase другом Base, но это кажется неправильным. Я не уверен, зачем TempBase нужен доступ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...