【问题标题】:Member function a friend会员功能朋友
【发布时间】:2015-09-29 07:48:07
【问题描述】:

我一直在尝试书中的一些示例(Stanley Lippman 的 C++ Primer) 我知道一个类可以让另一个类成为它的朋友(访问一些私有成员)。现在我正在阅读有关成员函数是朋友的信息,我尝试了这个例子

class Screen
{
public:
    friend void Window_mgr::clear();

    typedef std::string::size_type pos;

    Screen () = default;
    Screen (pos ht, pos wd, char c) : height (ht), width (wd),
                                      contents (ht * wd, c) { }

private:
    void do_display (std::ostream &os) const
    {
        os << contents;
    }

    pos cursor = 0;
    pos height = 0, width = 0;
    pos test_num = 100, test_num2 = 222;;
    std::string contents = "contents";
   };

  class Window_mgr {
 public:
     using ScreenIndex = std::vector<Screen>::size_type;
     void clear (ScreenIndex);

 private:
     std::vector <Screen> screens {Screen (24, 80, ' ')};
 };

 void Window_mgr::clear(ScreenIndex i)
{
Screen &s = screens[i];
s.contents = std::string(s.height * s.width, ' ');
}

但它会产生编译器错误提示

Window_mgr 尚未声明

然后我读到了这个:

• 首先,定义Window_mgr 类,它声明但不能定义clear。必须先声明 Screen,clear 才能使用 Screen 的成员。

• 接下来,定义类 Screen,包括一个用于 clear 的朋友声明。

• 最后,定义 clear,它现在可以引用 Screen 中的成员。

我不明白这部分 - 谁能解释一下?

【问题讨论】:

    标签: c++ class friend


    【解决方案1】:

    当编译器到达friend void Window_mgr::clear(); 时,它不知道Window_mgr 是什么,因为它还没有看到。您需要稍微重新排序以使其正常工作。首先你转发声明Screen 然后你有你Window_mgr

    class Screen;
    
    class Window_mgr {
    public:
        using ScreenIndex = std::vector<Screen>::size_type;
        void clear(ScreenIndex);
        Window_mgr();
    
    private:
        std::vector <Screen> screens;  // don't initialize here as we don't know what a screen actually is yet
        //std::vector <Screen> screens {Screen (24, 80, ' ')}; can't do this as we don't what a Screen is here
    };
    

    那你就可以Screen上课了

    class Screen
    {
    public:
        friend void Window_mgr::clear(ScreenIndex);
    
        typedef std::string::size_type pos;
    
        Screen() = default;
        Screen(pos ht, pos wd, char c) : height(ht), width(wd),
            contents(ht * wd, c) { }
    
    private:
        void do_display(std::ostream &os) const
        {
            os << contents;
        }
    
        pos cursor = 0;
        pos height = 0, width = 0;
        pos test_num = 100, test_num2 = 222;
        std::string contents = "contents";
    };
    

    然后你可以拥有Window_mgr 中使用Screen 的部分

    Window_mgr::Window_mgr() : screens{ Screen(24, 80, ' ') } {}
    
    void Window_mgr::clear(ScreenIndex i)
    {
        Screen &s = screens[i];
        s.contents = std::string(s.height * s.width, ' ');
    }
    

    您可以在 live example 中看到这一切都在运作

    【讨论】:

    • 一件事为什么要在 Window_mgr 中声明构造函数
    • @KrysselTillada 需要声明构造函数,因为您默认在类声明中创建 screens 并且您不能这样做,因为编译器不知道 Screen 是什么时间。
    • @LightnessRacesinOrbit 我认为Screen 甚至不会在using ScreenIndex = std::vector&lt;Screen&gt;::size_type; 中进行评估,因为它会影响结果。 AFAIK size_type 不依赖于类型。
    • @NathanOliver:作为一个成员,它无法摆脱依赖于类型的问题,我原以为...考虑专业化。编译器如何知道size_type 是什么以及它是否存在?我想到那时任何专业都必须在范围内,并且该名称实际上不是从属名称……这真的足够了吗?稍后可能会发布一个关于此的问题。
    • 感谢您的回答!我在这里被相同的代码困住了,但我的问题有点不同。我了解前向声明部分,我的问题是在类Window_mgr 之外定义构造函数是初始化数据成员screens 的唯一方法吗? (这意味着类内初始化程序不能像在您的代码注释中那样在此处使用,因为Screen 是不完整类型。)为什么可以将不完整类型用作向量模板的参数?
    【解决方案2】:

    您已经完成了大部分工作,您只需要确保在需要声明之前声明所有类和函数。

    第一个指令点说定义Window_mgr 类声明Window_mgr::clear。为了使用Screen,该类也必须在Window_mgr 之前声明。这看起来像:

    class Screen; //forward-declare Screen so that Window_mgr knows it exists
    
    class Window_mgr {
    public:
        //requires forward declaration of Screen, like the above
        using ScreenIndex = std::vector<Screen>::size_type;
        void clear (ScreenIndex); //declare, but don't define, clear
    };
    

    第二点说定义Screen 并包含Window_mgr::clear 的朋友声明。因为上面已经声明了该成员函数,所以这是有效的:

    class Screen
    {
    public:
        using ScreenIndex = std::vector<Screen>::size_type;
        //remember to friend with the same arguments
        friend void Window_mgr::clear(ScreenIndex);
        //...
    };
    

    最后一点告诉你现在定义Window_mgr::clear。我们已经在第一点声明了,所以我们只需要这样做:

    void Window_mgr::clear(ScreenIndex i)
    {
        //...
    }
    

    【讨论】:

      【解决方案3】:

      一个问题是您的clear 方法的签名不同。 Screen 类中声明的一个接受参数,而另一个不接受。签名必须相同,否则语言实际上将它们视为不同的函数。

      第二个问题是您的实现与您的第一点冲突:“定义Window_mgr 类,它声明但不能定义clear。”您的Window_mgr 类既声明又定义clear

      【讨论】:

        猜你喜欢
        • 2011-09-24
        • 1970-01-01
        • 2014-05-26
        • 2015-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-17
        相关资源
        最近更新 更多