I was reading C++ Primer recently and was trapped by the same code in this question (with a little difference), but my question is different. I know that there are a lot of similar questions, but after searching around, it seems no answer could answer my question.
At the very beginning, the code worked fine when I didn't add the friend member function.
/* First class */
class Screen {
public:
using pos = std::string::size_type;
using content_type = char;
Screen() = default;
Screen(pos ht, pos wd, content_type c)
: height(ht), width(wd), contents(ht * wd, c) {}
// Other inline functions are omited.
private:
pos width = 0;
pos height = 0;
std::string contents;
};
/* Second class */
class Window_mgr {
public:
using screen_index = std::vector<Screen>::size_type;
private:
std::vector<Screen> screens{Screen(24, 80, ' ')}; // Use `Screen` ctor
};
Then, I want to add the friend member function clear
. I wrote two versions of code according to code near page 281 in the book. The first version uses in-class initializer to initialize the vector
data member of Window_mgr
, while the second version uses constructor initializer list.
The first version (error) just like what the book wrote:
Making a member function a friend requires careful structuring of our programs to accommodate interdependencies among the declarations and definitions. In this example, we must order our program as follows:
- First, define the
Window_mgr
class, which declares, but cannot defineclear
.Screen
must be declared beforeclear
can use the members ofScreen
.- Next, define class
Screen
, including a friend declaration forclear
.- Finally, define
clear
, which can now refer to the members inScreen
.
/* Part 1, usually Window_mgr.h */
class Screen;
class Window_mgr {
public:
using screen_index = std::vector<Screen>::size_type;
void clear(screen_index);
private:
std::vector<Screen> screens{Screen(24, 80, ' ')}; // Error: No ctor for `Screen`
};
/* Part 2, usually Screen.h */
class Screen {
friend void Window_mgr::clear(screen_index);
public:
using pos = std::string::size_type;
using content_type = char;
Screen() = default;
Screen(pos ht, pos wd, content_type c)
: height(ht), width(wd), contents(ht * wd, c) {}
// Other inline functions are omited.
private:
pos width = 0;
pos height = 0;
std::string contents;
};
/* Part 3, usually Window_mgr.cpp */
void Window_mgr::clear(screen_index i) {
Screen &s = screens[i];
s.contents = std::string(s.height * s.width, ' ');
}
The second version (no error), which inspired by this answer:
/* Part 1, usually Window_mgr.h */
class Screen;
class Window_mgr {
public:
using screen_index = std::vector<Screen>::size_type;
void clear(screen_index);
Window_mgr(); // Add ctor declaration
private:
std::vector<Screen> screens; // Remove in-class initializer
};
/* Part 2, usually Screen.h */
class Screen {
friend void Window_mgr::clear(screen_index);
public:
using pos = std::string::size_type;
using content_type = char;
Screen() = default;
Screen(pos ht, pos wd, content_type c)
: height(ht), width(wd), contents(ht * wd, c) {}
// Other inline functions are omited.
private:
pos width = 0;
pos height = 0;
std::string contents;
};
/* Part 3, usually Window_mgr.cpp */
Window_mgr::Window_mgr() : screens{Screen(24, 80, ' ')} {} // Add ctor definition
// Because `Screen` is already a complete type, we can use its ctor now
void Window_mgr::clear(screen_index i) {
Screen &s = screens[i];
s.contents = std::string(s.height * s.width, ' ');
}
Note that I put all 3 parts of code in a single file for convenience, they can also be split into three different files with proper include guards and include header files.
I understand forward declaration and the include guards, so I'm not asking about them, here are my questions.
Is constructor defined outside class
Window_mgr
the only way to initialize the data memberscreens
if I want to use the classScreen
directly (not pointer or reference)?I think the in-class initializer cannot be used in the first version because
Screen
is incomplete type when its ctor is called.Why an incomplete type (
Screen
) can be used as a parameter to thevector
template?
Possible similar questions:
- Member function a friend
- Friend Class or Friend member function - Forward declaration and Header inclusion
- Why aren't my include guards preventing recursive inclusion and multiple symbol definitions?
I was trying to be clearly but seems a little bit verbose, anyway thanks for reading and helping :)