【问题标题】:Friend functions - Declaration OrderFriend 函数 - 声明顺序
【发布时间】:2016-01-19 10:33:51
【问题描述】:

我有两个班级,分别是 ScreenWindow_mgr

Screen 允许Window_mgr 通过友元函数声明修改其私有/受保护成员。

因此Window_mgr 在代码的最后定义了一个名为Window_mgr::clear 的非成员函数,它应该可以使用它。

不幸的是,我遇到了一些我自己无法解释的荒谬错误。

我错过了什么?

屏幕.h

#pragma once

#ifndef SCREEN_H
#define SCREEN_H

#include <string>
#include <vector>

class Window_mgr {
public:
    // location ID for each screen on the window
    using ScreenIndex = std::vector<Screen>::size_type;
    // reset the Screen at the given position to all blanks
    void clear(ScreenIndex);
private:
    std::vector<Screen> screens{ Screen(24, 80, ' ') };
};

class Screen {

public:

    // Friends
    friend void Window_mgr::clear(ScreenIndex);
    //friend class Window_mgr;

    // Fields
    // typedef => creates an alias
    // typedef std::string::size_type pos;
    // alternative way to declare a type member using a type alias
    using pos = std::string::size_type;

    // Constructors
    Screen() = default; // needed because Screen has another constructor
                        // cursor initialized to 0 by its in-class initializer
    Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {} // get the character at the cursor
    Screen &display(std::ostream &os) // function is in the class body => implicitly inline
    {
        do_display(os);
        return *this;
    }
    const Screen &display(std::ostream &os) const // function is in the class body => implicitly inline
    {
        do_display(os);
        return *this;
    }

    // Methods
    char get() const { return contents[cursor]; } // implicitly inline
    inline char get(pos ht, pos wd) const; // explicitly inline
    Screen &move(pos r, pos c); // can be made inline later
    Screen &set(char);
    Screen &set(pos, pos, char);

private:
    // Fields
    mutable size_t access_ctr;
    pos cursor = 0;
    pos height = 0, width = 0;
    std::string contents;

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

inline Screen &Screen::set(char c)
{
    contents[cursor] = c; // set the new value at the current cursor location
    return *this; // return this object as an lvalue
}

inline Screen &Screen::set(pos r, pos col, char ch)
{
    contents[r*width + col] = ch; // set specified location to given value
    return *this; // return this object as an lvalue
}

// we can specify inline on the definition
inline Screen &Screen::move(pos r, pos c) {
    pos row = r * width; // compute the row location
    cursor = row + c; // move cursor to the column within that row
    return *this; // return this object as an lvalue
}

char Screen::get(pos r, pos c) const // declared as inline in the class
{
    pos row = r * width; // compute row location
    return contents[row + c]; // return character at the given column
}

void Window_mgr::clear(ScreenIndex i)
{
    // s is a reference to the Screen we want to clear
    Screen &s = screens[i];
    // reset the contents of that Screen to all blanks
    s.contents = string(s.height * s.width, ' ');
}

#endif

【问题讨论】:

  • 你不缺#include &lt;vector&gt;#include &lt;iostream&gt;吗?
  • 抱歉忘记复制它的最新版本,即使包含矢量,我仍然收到有关“屏幕:未声明的标识符”等错误消息。
  • 我在文件顶部添加了一个class Screen; 来声明Screen,但这并不能完全解决问题。
  • class Screen; 如果你想做vector&lt;Screen*&gt; 可以解决它,但对于vector&lt;Screen&gt;,完整的Screen 必须可用。

标签: c++ header-files friend-function


【解决方案1】:

您不能在 Window_mgr 类中声明 Screen 对象的向量,因为此时编译器在您的代码中不知道 Screen。如果要声明一个指针向量,可以通过前向声明 Screen 来修复它,但对于实际对象的向量,必须有完整的定义。

你需要切换Window_mgrScreen的顺序,向Window_mgr类声明友谊:

class Screen {
public:
    friend class Window_mgr;
    ...
};
class Window_mgr {
public:
    // location ID for each screen on the window
    using ScreenIndex = std::vector<Screen>::size_type;
    // reset the Screen at the given position to all blanks
    void clear(ScreenIndex);
private:
    std::vector<Screen> screens{ Screen(24, 80, ' ') };
};

为什么编译器知道Window_mgr而不知道Window_mgr::ScreenIndex

C++ 对友谊声明中使用的类名有一个特殊规则:

如果友元声明中使用的类名尚未声明,则当场前向声明。

这就是编译器“知道”Window_mgr 的方式(即它不知道;它需要你的话)。在“友好”类中声明的成员函数或成员类型没有这样的规则。这就是为什么编译器不知道Window_mgr::ScreenIndex

【讨论】:

  • 非常感谢您的回答 - 这是我最终找到的解决方案之一。不幸的是,这不适用于friend void Window_mgr::clear(ScreenIndex); 而不是friend class Window_mgr; - 这是因为它自己的友元函数声明引用了Window_mgrScreenIndex - 知道如何避免这种交叉引用吗?
  • 是的,还是同样的错误。无论如何,我认为会有一个简单的解决方案。虽然我仍然不能完全理解为什么friend class Window_mgr 没有问题但friend void Window_mgr::clear(Window_mgr::ScreenIndex); 没有问题。两者都引用Window_mgr 或其成员之一,此时编译器不应该知道这些成员。
  • @HansMusterWhatElse 为了转发声明一个函数,编译器需要定义参数类型,在您转发声明 Window_mgr::clear(Window_mgr::ScreenIndex) 时编译器没有'不知道 Window_mgr::ScreenIndex 是什么,您将 Window_mgr 的定义推迟到 Screen 定义之后。
  • 那么为什么编译器知道Window_mgr而不知道Window_mgr::ScreenIndex?基于它与friend class Window_mgr; 完美配合的知识
  • @HansMusterWhatElse 请参阅编辑以了解此差异的说明。
【解决方案2】:

我建议不要为此使用“朋友”.. 只需向 Screen 类添加一个 clear() 公共成员函数,然后从窗口管理器中调用它

// declaration
Screen &clear();

// definition
inline Screen &Screen::clear() {
    contents.resize(height * width, ' ');
    return *this; // return this object as an lvalue
}

// use
void Window_mgr::clear(ScreenIndex i)
{
    // s is a reference to the Screen we want to clear
    Screen &s = screens[i];
    // reset the contents of that Screen to all blanks
    s.clear();
}

Coliru观看直播

【讨论】:

    猜你喜欢
    • 2013-05-09
    • 1970-01-01
    • 2021-09-19
    • 2011-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多