Класс User с возможностью выгрузки списка юзеров в файл
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.
Всем доброго времени суток.
Пишу класс
User
, который хранит в себе имя пользователя и его рекорд в игре. Так же должны быть функции считывания списка пользователей из файла( список —vector<User>
) и запись списка в файл.Пишу код:
Файл main.cpp — тестирование класса
User
в форме простенькой БДФайл
User.h
— определение классаUser
Файл
User.cpp
— реализация классаUser
Примечание: функция
getProgWay()
возвращает путь к исполняемой программе, определена вfunc.h
.Программа работает на ура, если файл пуст или его вообще нет. Но совершенно не хочет работать, если в файл уже была запись.
Программа вылетает( вообще полностью ) где-то между объявлением
list
и цикломwhile
. В чём проблема?UPD
Вставил более новую версию кода
Программа валится в
getUserList()
здесь:Когда вызывается
getUserList()
, векторlist
не содержит элементов. СледовательноuserList[i]
обращается к несуществующему элементу, тем более, что дляvector
операция[]
не проверяет валидность индекса, в отличие отvector<>::at()
. Пользуйсяpush_back()
, как вmain()
.И наконец, пользуйся отладчиком!
Не могу пройти мимо чужого огорода, что бы не кинуть пары камней ;-)
(1) Класс
User
можно совершенно спокойно делать структурой, попутно избавившись от четырёх методов:setScore()
,setName()
,getScore()
,getName()
. Закрытые данные и методы геттеры/сеттеры нужны только в том случае, если надо либо закрыть доступ к данным, либо при (как правило) установке данных (метод-сеттер) делается какая-то дополнительная работа. У тебя сейчас всё совершенно прозрачно и вышеперечисленные методы тривиальны. (Может, конечно, ты планируешь в будущем делать в этих методах что-то более сложное...)(2) В классе
User
встречаемся с тем же вопросом, который я поднимал в отношенииPascalString
— сериализация/десериализация. ПроPascalString
ты мне ответил, что за сериализацию должен отвечать вызывающий код. Вот теперь смотрим, что из этого получается.Класс
User
, с моей точки зрения, имеет два кривых костыля:Эти функции работают как с внутренними данными типа
User
, так и с контейнером. Нехорошо.Почему в классе не определить методы?
Или не перегрузить для класса
User
операции<<
и>>
? IMHO это логично и удобно:Понятно, что этот код схематичен, чисто для иллюстрации.
Чтение и запись конечно нужно упаковать в функции. Только никаких дружеских отношений эти функции к классу
User
не должны иметь. Это уже другой уровень абстракции: это — работа с вектором объектов типаUser
.(3) Кстати, обрати внимание, что
vector<>::size()
возвращает отнюдь неint
.(4) Таких вот вещей тоже лучше не делать, тем более, что этот литерал у тебя используется минимум в двух местах.
Сделай этот литерал именованной константой.
(5) Если уж ты используешь такой мощный класс для исключений, то...
(5.1) Разнеси его на два файла: заголовочный и реализацию (как минимум, метод
what()
).(5.2) Если используешь перечисление, то либо пользуйся
enum class
(C++11), либо объявляй перечисление внутри классаUserExeption
. Тогда обращение к члену перечисления будет выглядеть какUserExeption::FILE_NOT_OPEN
, но зато гарантированно избежишь конфликта имён.(6) Функция
getUserList()
НЕ должна предполагать, чтоlist
пустой.Вот.
Я пытался, только копание в плохо понятном мне ассемблерном коде нисколько не помогло. :(
В данном случае, не имеет смысла, поскольку чтение и вывод в поток будут производиться всего один раз — в функциях
getUserList()
иwriteUserList()
.То есть как-то так?:
User.h
и файл
User.cpp
А разве это не будет равносильно:
То есть будут потери памяти. Или нет?
Я тебя не призываю копаться в окне ассемблерного кода, не зная ассемблера )) Но тебе ни что не мешает расставить точки останова в исходном коде на С++, при остановах посмотреть значения переменных, установить наблюдение за переменными, пошагово пройти подозрительные места и пр.
Тогда у тебя не будет возникать вопросов типа у меня программа крашится где-то между Москвой и Воронежем...
Кстати, в плане отладки Code::Blocks сделан получше, чем Dev-C++. Хотя оба используют и компилятор, и отладчик из MinGW. (Я, например, к Code::Blocks прикрутил компилятор, входящий в комплект поставки Dev-C++.)
С точки зрения идеологии ООП — совершенно необдуманное заявление. Объект должен сам обслуживать свои данные и следить за их целостностью.
Ну да, примерно так. Я бы, единственно, оставил конструктор по умолчанию — не люблю неинициализированные переменные. И, см. выше, добавил бы перегруженные операции
<<
и>>
.Интересно, что при чтении из файла ты проверяешь каждый чих и выдаёшь, если надо исключения, а при записи файла считаешь, что если файл успешно открыт, то и запись пройдёт без проблем.
И вот это
выглядит опасно, поскольку функция
getProgWay()
будет вызываться до начала работыmain()
. Я бы оставил так:а формирование полного имени файла делал в
main()
или каком-то другом подходящем месте (например в функции, которая делает первичную инициализацию игры).Не будет равносильно. В твоём варианте каждый экземпляр класса
UserExeption
будет иметь два константных члена типаint
. А когда используется перечисление (enum
), символические имена в нужных местах заменяются компилятором на непосредственные значения. Т.е. объявлениеenum En { E1 = 0, E2, E3, E4 = 100500 ... }
кода вообще не генерирует. В этом смысле, аналогичное поведение у#define E4 100500
, только#define
обрабатывает препроцессор, аenum
— компилятор.Hint. Что бы при copy-paste из Dev-C++ кириллические символы не превращались в кракозябры, перед копированием переключись в Dev-C++ на ввод кириллицы.
Но эта функция инициализации игры находится находится слишком далеко от реализации
User
. Как быть?А почему опасно? У меня
main
только и делает, что объявляет объект и вызывает его метод.Аргументированно ответить пока не могу. Но я бы так делать не стал. На уровне интуиции.
На код
getProgWay()
можно посмотреть?А в чём проблема?
User
у тебя вообще лежит в двух отдельных исходниках. Оно ото всего одинаково далеко ))Когда ты инициализируешь игру, вычисли полное имя файла для результатов и сохрани в член класса игры (константный частичный путь тоже можно объявить где-то рядом). А когда нужно будет подгрузить или выгрузить результаты, в соответствующую функцию (или метод этого класса) просто передай переменную с полным именем файла результатов в качестве параметра.
А так не проще вместо вызова
GetModuleFileName()
использоватьargv[0]
?Ну дак много лишней мороки будет с передачей этого
argv[0]
. А так всё просто.No way :) Скорее GetProgPath, или GetPath(). Используй
argv[0]
, как советовал Череп, оно хотя-бы кроссплатформенное.Могу предложить создать отдельный класс логгера, туда передать полный путь к argv[0] и вызывать ее там, где нужно. Потом ты сможешь реализовать удаленное логирование, либо через системный журнал, не меня вызовы записи логов по всей игре.
Опять же, правильно — Exception. И лучше реализовать собственное исключение через расширение стандартного:
в игре столько вещей привязанных к платформе
windows
, чтоGetModuleFileName
является лишь каплей в море...А что это даёт?
Все исключения в C++ наследуются от
std::exception
. У него есть стандартизированный интерфейс, методwhat()
и т.п.Во первых, это избавляет тебя от написания собственной реализации, во вторых делает твой код мягким и шелковистым для других программистов :)
Кстати, логирования — такая штука, где параллельность просто необходима. Попробуй написать асинхронный логгер, который не будет тормозить игру, записывая данные на диск в отдельном потоке. Сделать его потокобезопасным (т.е., чтобы один объект мог использоваться параллельно в разных участках кода). Можно также сделать очередь сообщений и разделить типы логов (ошибки и отладочная информация), сделав возможно записывать два лога параллельно.
Книжку читал, что сбрасывал Croessmah?
Ещё нет, читаю Дейтел Х. Дейтел П — Как программировать на С++. :)
Потом почитай обязательно. Книжка хорошая очень.
ок :)