友元函数 - 声明顺序
Friend functions - Declaration Order
我有两个 类,分别是 Screen
和 Window_mgr
。
Screen
允许 Window_mgr
通过友元函数声明修改其私有/受保护成员。
因此 Window_mgr
在代码的最后定义了一个名为 Window_mgr::clear
的非成员函数,它应该使用它。
不幸的是,我遇到了一些我自己无法解释的荒谬错误。
我错过了什么?
Screen.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
您不能在 Window_mgr
class 中声明 Screen
个对象的向量,因为编译器在您的代码中此时不知道 Screen
。如果你要声明一个指针向量,你可以通过 forward-declaring Screen
来修复它,但是对于一个实际对象的向量,必须有完整的定义。
你需要调换Window_mgr
和Screen
的顺序,向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 not Window_mgr::ScreenIndex
C++ 对友谊声明中使用的 class 名称有特殊规则:
If the name of the class that is used in the friend declaration is not yet declared, it is forward declared on the spot.
这就是 Window_mgr
的编译器 "knows" 的方式(即它没有;它相信您的话)。对于在 "befriended" classes 中声明的成员函数或成员类型没有这样的规则。这就是编译器不知道 Window_mgr::ScreenIndex
.
的原因
我建议不要为此使用 "friend".. 只需将 clear() public 成员函数添加到 class Screen,然后从 window 管理器调用它
// 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
现场观看
我有两个 类,分别是 Screen
和 Window_mgr
。
Screen
允许 Window_mgr
通过友元函数声明修改其私有/受保护成员。
因此 Window_mgr
在代码的最后定义了一个名为 Window_mgr::clear
的非成员函数,它应该使用它。
不幸的是,我遇到了一些我自己无法解释的荒谬错误。
我错过了什么?
Screen.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
您不能在 Window_mgr
class 中声明 Screen
个对象的向量,因为编译器在您的代码中此时不知道 Screen
。如果你要声明一个指针向量,你可以通过 forward-declaring Screen
来修复它,但是对于一个实际对象的向量,必须有完整的定义。
你需要调换Window_mgr
和Screen
的顺序,向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++ 对友谊声明中使用的 class 名称有特殊规则:
If the name of the class that is used in the friend declaration is not yet declared, it is forward declared on the spot.
这就是 Window_mgr
的编译器 "knows" 的方式(即它没有;它相信您的话)。对于在 "befriended" classes 中声明的成员函数或成员类型没有这样的规则。这就是编译器不知道 Window_mgr::ScreenIndex
.
我建议不要为此使用 "friend".. 只需将 clear() public 成员函数添加到 class Screen,然后从 window 管理器调用它
// 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
现场观看