Недавно я читал С++ Primer и оказался в ловушке того же кода в этом вопросе (с небольшой разницей), но мой вопрос другой. Я знаю, что есть много подобных вопросов, но после поиска, кажется, ни один ответ не может ответить на мой вопрос.
В самом начале код работал нормально, когда я не добавлял функцию члена-друга.
/* 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
};
Затем я хочу добавить функцию члена-друга clear
. Я написал две версии кода в соответствии с кодом около страницы 281 в книге. Первая версия использует инициализатор в классе для инициализации элемента данных vector
Window_mgr
, а вторая версия использует список инициализаторов конструктора.
Первая версия (ошибка), как и то, что написала книга:
Создание функции-члена для друга требует тщательной структуризации наших программ для учета взаимозависимостей между декларациями и определениями. В этом примере мы должны заказать нашу программу следующим образом:
- Сначала определите класс
Window_mgr
, который объявляет, но не может определитьclear
.Screen
должен быть объявлен до того, какclear
может использовать членыScreen
.- Затем определите класс
Screen
, включая объявление друга дляclear
.- Наконец, определите
clear
, который теперь может ссылаться на члены вScreen
.
/* 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, ' ');
}
Вторая версия (без ошибок), которая вдохновлена этим ответом:
/* 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, ' ');
}
Обратите внимание, что я поместил все 3 части кода в один файл для удобства, их также можно разделить на три разных файла с надлежащим включением охранников и включить файлы заголовков.
Я понимаю форвардную декларацию и включаю охранников, поэтому я не спрашиваю о них, вот мои вопросы.
-
Является ли конструктором вне класса
Window_mgr
единственным способом инициализировать элемент данныхscreens
, если я хочу использовать классScreen
напрямую (не указатель или ссылку)?Я думаю, что инициализатор в классе нельзя использовать в первой версии, потому что
Screen
является неполным типом при вызове его ctor. -
Почему неполный тип (
Screen
) можно использовать в качестве параметра для шаблонаvector
?
Возможные похожие вопросы:
- Функция участника друга
- Функция класса друга или друга - объявление вперед и включение заголовка
- Почему мои защитники не включают рекурсивное включение и множественные определения символов?
Я старался быть ясно, но, похоже, немного подробный, в любом случае спасибо за чтение и помощь:)