Reputation: 671
I have two classes called Screen
and Window_mgr
.
Screen
is allowing Window_mgr
to modify its private / protected members via the friend function declaration.
As a result Window_mgr
defines at the very end of the code a non-member function called Window_mgr::clear
which is supposed to make use of it.
Unfortnately, I get some ridiculous errors which I can't explain myself.
What am I missing?
#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
Upvotes: 3
Views: 550
Reputation: 726809
You cannot declare a vector of Screen
objects inside your Window_mgr
class, because Screen
is not known to the compiler at that point in your code. If you were to declare a vector of pointers, you could fix it by forward-declaring Screen
, but for a vector of actual objects full definition must be available.
You need to switch around the order of Window_mgr
and Screen
, and declare friendship to Window_mgr
class:
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, ' ') };
};
why does the compiler know
Window_mgr
but notWindow_mgr::ScreenIndex
C++ has a special rule for class names used in friendship declarations:
If the name of the class that is used in the friend declaration is not yet declared, it is forward declared on the spot.
That is how the compiler "knows" of Window_mgr
(i.e. it does not; it takes your word for it). There is no such rule for member functions or member types declared inside "befriended" classes. That is why the compiler does not know about Window_mgr::ScreenIndex
.
Upvotes: 5
Reputation: 11582
I recommend not using "friend" for this.. just add a clear() public member function to class Screen, and call it from the window manager
// 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();
}
See it live in Coliru
Upvotes: 0