C ++: нежелательные преобразования между ostream и ofstream - PullRequest
0 голосов
/ 24 октября 2018

Я работаю над приложением для личного словаря, которое поможет вам запомнить слова, которые вы выучили.Он работает через CLI (просто не задавайте вопросов, это всего лишь тест, и у меня возникла странная страсть к приложениям CLI).Поэтому, конечно, я использую ostream s для записи информации о CLI.Я привык писать operator<< перегрузки (для ostream с) для каждого класса, чтобы я мог создать многоуровневую систему вывода (в основном, каждый объект может «говорить» за себя).

InЧтобы сохранить объект словаря, я хотел использовать ofstream и написать файл с ним.Естественно, я написал operator<< перегрузки также для ofstream и в той же «многоуровневой» структуре.

В результате у меня теперь есть две operator<< перегрузки в каждом классе, как в "Dictionary":

ostream& operator<<(ostream&, const Dictionary&);
ofstream& operator<<(ofstream&, const Dictionary&);

(это просто объявление в заголовочном файле)

Обратите внимание, что очень важно, чтобы обе эти перегрузки делали разные вещи. Я не хочу, чтобы в моем файле был какой-то странный ориентированный на постоянство текст специального формата, а также не удобный для пользователя простой текст в моем файле.

Проблема в том, что из-за наследованияструктура ostream и ofstream, ofstream иногда косвенно преобразуется в ostream.И когда это происходит в середине моего стека, заполненного операциями вывода file , программа неожиданно переходит к неправильной перегрузке и печатает простой текст в файле.

Мой вопрос прост:Есть ли способ избежать или отменить эти нежелательные неявные преобразования, чтобы позволить моей программе перейти в правильные перегрузки?Или есть какой-то другой хороший способ исправить эту проблему?

РЕДАКТИРОВАТЬ 1 :

Кто-то указал в комментариях, что это не является неявной конверсией.ofstream иногда "рассматривается" как его базовый класс ostream.Проблема в том, что в какой-то момент объект «забывает», что это ofstream, и теряет всю информацию, связанную с файлом.С этого момента это только ostream, и именно это я имел в виду под «преобразованием».

РЕДАКТИРОВАТЬ 2:

Точная точка в программе, где«нежелательное преобразование» происходит здесь:

ofstream& operator<<(ofstream& of, const Section& s) {
    return s.print_ofstream(of);
}

Так что этот оператор overoad вызывает «print_ofstream»:

ofstream& Section::print_ofstream(ofstream& of) const {
    of << "sec" << Util::ID_TO_STRING(section_id) << ":\n";
    for (pair<Wordlist, Wordlist> pwl : translations) {
        of << '{' << pwl.first << '=' << pwl.second << "}\n";
    }
    of << "#\n";
    return of;
}

Обратите внимание, что «pwl» - это пара из двух Wordlist с, поэтому pwl.first / pwl.second равен Wordlist.Таким образом, обычно строка of << '{' << pwl.first << '=' << pwl.second << "}\n"; должна вызывать перегрузку ofstream operator<< в Wordlist.Но это не так.Вместо этого вызывается другой метод перегрузки:

ostream& operator<<(ostream& o, const Wordlist& wl) {
    return wl.print_ostream(o);
}

1 Ответ

0 голосов
/ 25 октября 2018

Вы перегрузили только определенные operator<<, необходимые для потоковой передачи Dictionary, Section, Wordlist и т. Д. Объектов в std::ofstream, но std::ofstream наследует МНОГИЕ другие operator<< s из std::ostream, и все эти операторы принимают ostream& в качестве ввода и возвращают ostream& в качестве вывода.Так, например, of << "sec" вернет ostream&, даже если of является std::ofstream, а затем этот ostream& используется для последующих вызовов <<, пока не будет достигнут ;.Это «неявные преобразования», которые вы испытываете.

Реальный вопрос в том, ПОЧЕМУ вы хотите, чтобы operator<< выводил разные данные в зависимости от типа std::ostream, в который производится запись?Это идет вразрез с потоковой моделью C ++.Если вы действительно этого хотите, вам придется изменить print_ofstream(ofstream&) на print_ostream(ostream&), а затем динамически определять фактический производный тип std::ostream, используя dynamic_cast.То же самое с Wordlist и любыми другими классами, которым это необходимо.

Более простой и безопасный вариант - просто хранить флаг внутри ваших классов, чтобы контролировать способ вывода их данных, независимо от типаstd::ostream используется.Затем вы можете установить этот флаг по мере необходимости.Возможно, даже определите некоторые вспомогательные манипуляторы ввода / вывода для установки этих флагов при совершении вызовов <<.

...