Участник функции друга

Я пробовал несколько примеров в книге (С++ Primer от Stanley Lippman) и я понимаю, что класс может сделать другой класс своим другом (доступ к некоторым частным членам). Теперь я читаю о том, что функция-член является другом, и я пробую пример

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 сможет использовать элементы экрана.

• Затем определите экран класса, включая декларацию друга для очистки.

• Наконец, определите четкость, которая теперь может относиться к членам на экране.

Я не понимаю эту часть - может кто-нибудь объяснить?

Ответ 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

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, ' ');
}

Вы можете увидеть все это в этом живом примере

Ответ 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.