Объявите функцию-член заранее объявленного класса как друга

Можно ли объявить функцию-член заранее объявленного класса как друга? Я пытаюсь сделать следующее:

class BigComplicatedClass;

class Storage {
   int data_;
public:
   int data() { return data_; }
   // OK, but provides too broad access:
   friend class BigComplicatedClass;
   // ERROR "invalid use of incomplete type":
   friend void BigComplicatedClass::ModifyStorage(); 
};

Таким образом, цель состоит в том, чтобы (i) ограничить объявление друга одним методом и (ii) не включать определение сложного класса для сокращения времени компиляции.

Одним из подходов может быть добавление класса, выступающего в качестве посредника:

// In Storage.h:
class BigComplicatedClass_Helper;
class Storage {
    // (...)
    friend class BigComplicatedClass_Helper;
};

// In BigComplicatedClass.h:
class BigComplicatedClass_Helper {
     static int &AccessData(Storage &storage) { return storage.data_; }
     friend void BigComplicatedClass::ModifyStorage();
};

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

4 ответа

Решение

Как говорит @Ben, это невозможно, но вы можете предоставить конкретный доступ только к этой функции-члену через "пароль". Он работает немного как промежуточный вспомогательный класс, но imho более понятен:

// Storage.h
// forward declare the passkey
class StorageDataKey;

class Storage {
   int data_;
public:
   int data() { return data_; }
   // only functions that can pass the key to this function have access
   // and get the data as a reference
   int& data(StorageDataKey const&){ return data_; }
};

// BigComplicatedClass.cpp
#include "BigComplicatedClass.h"
#include "Storage.h"

// define the passkey
class StorageDataKey{
  StorageDataKey(){} // default ctor private
  StorageDataKey(const StorageDataKey&){} // copy ctor private

  // grant access to one method
  friend void BigComplicatedClass::ModifyStorage();
};

void BigComplicatedClass::ModifyStorage(){
  int& data = storage_.data(StorageDataKey());
  // ...
}

Нет, вы не можете объявить отдельные функции-члены как друзья, пока они не были объявлены. Вы можете подружиться только со всем классом.

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

Например, недавно мне нужно было закрыть (единый глобальный статический) системный журнал ошибок от глобального обработчика исключений на основе порта чужого кода. Обычный включаемый файл для моего журнала ошибок конфликтовал с кодом обработчика исключений, потому что оба хотели включить "windows.h" по причинам, которые я не изучал. Когда этот и другие вопросы убедили меня, я не смог сделать предварительное объявление функций-членов моего класса ErrorLog, я сделал так, что обернул необходимые функции в глобальную функцию области видимости:

void WriteUrgentMessageToErrorLog( const char * message )
{
  ErrorLog::LogSimpleMessage( message );
  ErrorLog::FlushAccumulatedMessagesToDisk();
}

Некоторые люди очень внимательно относятся к поддержанию целостности структуры своих классов любой ценой... и редко признают, что приложения, использующие эти классы, неизбежно создаются поверх чего-то, что не имеет такой структуры. Но это там, и используется разумно, у него есть свое место.

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

Можно ли объявить функцию-член заранее объявленного класса как друга?

Нет. Конечно, компилятор не знает о таких функциях-членах.

Другие вопросы по тегам