问题 声明前向声明的类的成员函数作为朋友


是否可以将前向声明的类的成员函数声明为朋友?我正在尝试执行以下操作:

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();
};

然而,这似乎有点笨拙...所以我认为必须有一个更好的解决方案!


3184
2018-06-10 18:44


起源

可能重复 如何在C ++中声明一个朋友是另一个尚未定义的类的成员函数? - Ben Voigt
谢谢你的参考 - 我看到了这个问题;但是它接受的答案是我想要避免的过于宽泛的类级别访问... - hrr


答案:


正如@Ben所说,这是不可能的,但你可以通过一个只对该成员函数提供特定的访问权限 “万能钥匙”。它的工作方式有点像中间帮助程序类,但更清晰:

// 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());
  // ...
}

11
2018-06-10 19:05



太好了!不可否认,我花了一段时间才明白为什么复制构造函数必须是私有的...... - hrr
@hrr:嗯,它不一定是,因为如果你愿意,你仍然可以传递密钥作为引用。 - Xeo
如果复制构造函数是公共的,那么没有特权的用户就可以轻松获得密钥: StorageDataKey *key_ptr = 0; StorageDataKey key(*key_ptr); 从而使复制构造函数私有化似乎很有意义。 - hrr
@hrr:nullpointer dereference的好点。它是未定义的行为,虽然它可以工作很长时间,你不访问任何成员(这不是这里的情况)。问题是,你不能只是改变参数 data 到非参考 StorageDataKey,因为你需要完整的定义。 :/在C ++ 0x中,您可以使用右值引用 StorageDataKey&&虽然我认为可以用简单的方法绕过它 std::move......嗯。 - Xeo
@hrr将其设为私有并不能解决问题,用户可以调用 storage_.data(*key_ptr)。我认为 StorageDataKey 应该有 int const id,应该签入 Storage  光栅接入前的等级。 - q126y


不,在声明个别成员函数之前,您不能将其声明为朋友。你只能与整个班级成为朋友。


3
2018-06-10 18:48





它在这里可能相关或不相关,但有必要提醒自己,有一个超出类和对象范围的狂野世界,其中函数可以自由漫游。

例如,我最近需要根据其他人代码的端口从全局异常处理程序中关闭(单例全局静态)系统错误日志。我的错误日志的正常包含文件与异常处理程序代码冲突,因为我想要包含“windows.h”,原因是我没有查看。当这个和其他问题说服我时,我无法向我的ErrorLog类的成员函数做出前向声明,我所做的是将必要的函数包装到这样的全局作用域函数中:

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

有些人非常注重不惜一切代价保持其类结构的完整性......并且很少承认使用这些类的应用程序不可避免地建立在缺乏该结构的东西之上。但它在那里,明智地使用,它有它的位置。

鉴于这个问题的年代,我没有深入研究它的相关性。我想要分享的一点是,有时像这样的简单包装机制是一种更清晰,更容易理解的替代品,它具有更多的微妙和聪明。微妙和聪明往往会被某些需要添加到其中并且不完全理解它的人改变。在你知道它之前,你有一个bug ...


2
2018-02-06 22:53





是否可以将前向声明的类的成员函数声明为朋友?

当然,编译器不了解这些成员函数。


0
2018-06-10 18:58