0

Chapter 7.3.4 of the book C++ Primer mentions forward declarations and specifying member function friends. It states that specifying member function friends requires careful structuring to accommodate interdependencies among the declarations and definitions. When I strip all the fluff, this is the result:

#include <string>
#include <vector>

class Screen;

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

class Screen {
    friend void Window_mgr::clear(ScreenIndex);
public:
    using pos = std::string::size_type;
private:
    pos height{0},
        width{0};
    std::string contents;
};

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

My understanding is that classes go into their own header files. If that is the case, how do I separate these? I checked out some of the questions on Stack Overflow, but did not find the answer I am looking for.

Edit: this is how I was able to strip up the code, but I'm still unsure whether this is good of bad practice and whether this is even maintainable.

window_mgr.h

#ifndef WINDOW_MGR_H
#define WINDOW_MGR_H

#include <vector>

class Screen;

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

#endif

screen.h

#ifndef SCREEN_H
#define SCREEN_H

#include <string>

#include "window_mgr.h"

class Screen {
    friend void Window_mgr::clear(ScreenIndex);
public:
    using pos = std::string::size_type;
private:
    pos height{0},
        width{0};
    std::string contents;
};

#endif

window_mgr.cpp

#include "window_mgr.h"
#include "screen.h"

inline void Window_mgr::clear(ScreenIndex i) {
    Screen& s = screens[i];
    s.contents = std::string(s.height * s.width, ' ');
}
L.S. Roth
  • 447
  • 2
  • 14
  • `void Window_mgr::clear(ScreenIndex i)` would go in Window_mgr.cpp – Jarod42 Sep 21 '21 at 12:48
  • Take the declaration of a class into a header and the definition of methods in a cpp file. Most times you will have less trouble with includes when every class is in a separate *.h/*.cpp file. – MiniMik Sep 21 '21 at 12:48
  • "My understanding is that classes go into their own header files." Not necessarily. C++ is not Java. You can place as many classes in a single header as you like, and often "one class per file" is very impractical – 463035818_is_not_an_ai Sep 21 '21 at 12:49
  • @Jarod42 Why does that function need its own translation unit? – L.S. Roth Sep 21 '21 at 12:49
  • 2
    Solution - avoid friends, let `Screen` define how it clears itself, and let `Window_mgr` use this public functionality of `Screen` – Alexey S. Larionov Sep 21 '21 at 12:50
  • This link explains the process of separation of class specification and definition (https://www.learncpp.com/cpp-tutorial/class-code-and-header-files/) – Karen Baghdasaryan Sep 21 '21 at 12:50
  • related/dupe: https://stackoverflow.com/questions/625799/resolve-build-errors-due-to-circular-dependency-amongst-classes – NathanOliver Sep 21 '21 at 12:52
  • @AlexeyLarionov that feels more intuitive, but I am wondering when to do it the way the book describes, whether that is even maintainable, and how to organise the code. – L.S. Roth Sep 21 '21 at 12:55
  • `Window_mgr::clear` need definition of `Window_mgr` (as it is one of its member) and `Screen` (it uses member of `Screen`). So should be after both definitions. Once you split in header file, simplest solution is in cpp file. – Jarod42 Sep 21 '21 at 12:55
  • circular dependency sucks - avoid it as much as possible. Cycles can be broken by introducing third class which those two classes depend on. – Marek R Sep 21 '21 at 13:18

1 Answers1

0

For what it's worth...

I've never had to use friend methods except operator<<. If you add getters on the private fields, then your Window_mgr method doesn't need to be a friend, and you eliminate the circular dependency.

Joseph Larson
  • 8,530
  • 1
  • 19
  • 36