【问题标题】:How do I make main a friend of my class from within a library?我如何在图书馆里让 main 成为我班上的朋友?
【发布时间】:2012-01-27 00:12:03
【问题描述】:

请看my first attempt at answering this .为了简化事情,我之前忽略了讲述整个故事。原来我的例子有效!对不起。

整个故事是,这是一个库,在一个文件中包含一个类,在另一个文件中包含主类,所有这些都链接到我的库中。该库为流程框架提供了基础,这就是为什么 main 位于库中而不是流程中的原因。

下面是我所拥有的精简版。

pf.hpp

using namespace std;

namespace MyNamespace
{
  class ProcessManager
  {
  public:
    friend int main(int argc, char** argv);
  private:
    void test();
  };
};

pf.cpp

#include "pf.h"

namespace MyNamespace
{
  ProcessManager::test()
  {
    cout << "My friend has accessed my member" << endl;
  }
};

pfmain.cpp

#include "pf.hpp"

int main(int argc, char** argv)
{
   ProcessManager pm;

   pm.test();
}

请注意,编译库时会失败

我试过的是:

  • 让朋友四处走动
  • 使朋友引用主要使用全局范围(例如 ::main)
  • 交友和主声明使用全局范围

我错过了什么?

谢谢!

【问题讨论】:

  • 这有点跑题了,但是很多人会说friend关键字从来都不是一个好的解决方案。
  • 我同意这并不理想,但有时你别无选择
  • 可能要考虑与main() 的其他变体加好友。但不确定他们是否在 Facebook 上。

标签: c++ main friend


【解决方案1】:

只需在MyNamespace之外声明main,并在friend语句中指定全局命名空间::

//in header file of ProcessManager
//your pf.h

int main(int argc, char** argv);

namespace MyNamespace
{
  class ProcessManager
  {
  public:
    friend int ::main(int argc, char** argv);
  private:
    void test();
  };
};

【讨论】:

  • 我不明白为什么编译器在我的情况下看不到 pfmain.cpp 中的 main ,但这有效。谢谢!
  • 之前的尝试会显示错误“error: 'int main(int, char**)' should have been declared inside '::'” 这应该已经足够说明问题了,但我永远不会考虑到头文件的设置方式,预计编译器不会检查其他 .o 文件中的符号..
  • C++ main() 中的@Jaime 必须在全局命名空间中声明。然而,该名称不是保留名称,可用于成员函数和命名空间等其他地方。
【解决方案2】:

@parapura 提供了一个解决方案,但没有解释为什么您首先必须在全局范围内声明 main

§7.3.1.2 [namespace.memdef] p3

[...] 如果非本地类中的 friend 声明首先声明了一个类或函数,则友元类或函数是最内层封闭命名空间的成员。 [...]

因此,考虑到这一点,您的代码将如下所示:

namespace MyNamespace
{ // MyNamespace is the innermost enclosing namespace
  // 'main' from the friend declaration is treated
  // as if it was a member of 'MyNamespace'
  int main(int argc, char** argv);

  class ProcessManager
  {
  public:
    friend int main(int argc, char** argv);
  private:
    void test();
  };
};

现在应该清楚为什么全局 main 函数不是你的朋友了。

【讨论】:

    【解决方案3】:

    一个常见的答案可能是提供一个“应用程序”单例类,例如QApplication 在 Qt 中,将你的 main 减少到类似

    int main (int argc, char** argv) {
        YourApplication app (argc, argv);
        return app.execute();
    }
    

    然后你将你的友谊问题减少到class YourApplication而不是你的其他班级,你知道该怎么做。

    【讨论】:

    • 这个类实际上已经是一个单例了,我只是没有展示那部分。拥有一个单例并不能解决问题..
    【解决方案4】:

    我不认为你真的想做你正在做的事情。这看起来真的像一个黑客和设计问题。如果你真的想在某些特殊情况下公开你的类的内部,你可以创建一个访问器类,它也在你的库中定义。

    这样的事情可能会起作用(可能需要适当的前向声明等——这只是一个起点):

    class ProcessManagerAccessor
    {
    public:
        ProcessManagerAccessor(ProcessManager & pm) : pm_(pm) { }
    
        // add public methods to expose internals
        void test() { pm_.test(); }
    
    private:
        ProcessManager & pm_;
    };
    
    class ProcessManager
    {
    public:
        friend class ProcessManagerAccessor;
    
        // ...
    };
    
    // ...
    
    ProcessManager pm;
    ProcessManagerAccessor pma(pm);
    pma.test();
    

    【讨论】:

    • "一般来说,你不想为了测试而破坏任何封装(或者就像妈妈常说的,“不要暴露你的隐私!”)。大多数时候,你应该能够通过执行它的公共方法来测试一个类。如果有重要的功能隐藏在私有或受保护的访问后面,这可能是一个警告信号,表明那里有另一个类正在努力摆脱。"
    • 我认为这与提供主要访问权限没有什么不同。问题是我的类是一个单例,没有参数实例化,但是在我将控制权交给 ProcessManager 的派生类之前,我需要在主中设置一些状态。我不希望派生类可以访问这些方法。
    • “test”方法只是一个例子......它实际上并没有用于测试
    • 授予main 访问权限听起来像是一个大锤解决方案——这意味着对于任何主要功能,您的所有隐私都将公开。这里有很多(自我强加的?)设计问题。如果您有不可测试的代码,最好的解决方案是通过设计使其可测试。对此的完整讨论可能超出了此 stackoverflow 问题的范围,但值得探索获得一些可测试性的标准技术。 Feathers 写了一整本书——这里有一个小摘要:objectmentor.com/resources/articles/…
    • Main 是我在库中创建并由我控制的,而不是由进程所在的 ProcessManager 的派生者。当然,您可以欺骗编译器选择您自己的 main,并且可能能够让它为您的 main 提升权限,但我现在不打算达到那种完整性。
    猜你喜欢
    • 1970-01-01
    • 2016-02-27
    • 2011-03-23
    • 1970-01-01
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 2011-01-24
    • 1970-01-01
    相关资源
    最近更新 更多