Есть правило, четыре способа состояние ошибки может быть передано от вызываемого к абоненту:
1. Прямое возвращаемое значение (код возврата или параметр OUT).
Код возврата невозможен для вызова конструктора, хотя параметр OUT. Тем не менее, для некоторых целей требуется, чтобы каждая функция предоставляла код возврата или параметр OUT для этой цели, поэтому мне не нравится это решение в целом, хотя оно, безусловно, сильно используется в различных библиотеках и API. Вы можете использовать этот подход, добавив указатель или ссылочный параметр к своему конструктору, которому вызывающий мог предоставить адрес некоторой локальной переменной ошибки, в которой конструктор мог сохранить возможное возвращаемое значение. Я не рекомендую этого.
2. Исключения.
Существует несколько поляризованная дискуссия о плюсах и минусах исключений, как в коде C++, так и на других языках. Я могу занять несколько голосов, чтобы сказать это, но мое личное мнение заключается в том, что исключений следует избегать, как чума. См. http://www.joelonsoftware.com/items/2003/10/13.html для тех, кто разделяет мой взгляд. Но это приемлемое решение, если вы так склонны. См. Ответ @ Брайана за хорошую демонстрацию этого решения.
3. Атрибут объекта.
Объект std::ifstream
на самом деле делает это, поэтому вы можете использовать это. (На самом деле, из вашего примера кода вы определяете свой std::ifstream
как локальную переменную в конструкторе, что означает, что он не будет сохраняться после вызова, но поскольку вы вызываете какие-то методы read()
и write()
на построенный объект, это означает, что вы do сохраняете это после вызова, поэтому я собираюсь предположить, что последнее является правильным выводом.) Вы можете использовать это, позвонив std::ifstream::is_open()
. Если вы хотите сохранить инкапсуляцию std::ifstream
, вы можете определить свой собственный эквивалент is_open()
на FileUtility
, который просто вернет in_stream.is_open();
, если он сохранен как атрибут вашего класса FileUtility
.
struct FileUtility {
ifstream ifs;
FileUtility(const char* fileName);
bool is_open(void) const;
};
FileUtility::FileUtility(const char* fileName) { ifs.open(fileName); }
bool FileUtility::is_open(void) const { return ifs.is_open(); }
FileUtility fu = FileUtility("bob.txt");
if (!fu.is_open()) return 1;
В качестве альтернативы, вы можете создать совершенно новое состояние ошибки слой только для FileUtility
класса, и распространять ошибку std::ifstream
через это. Например:
struct FileUtility {
static const int ERROR_NONE = 0;
static const int ERROR_BADFILE = 1;
ifstream ifs;
int error;
FileUtility(const char* fileName);
};
FileUtility::FileUtility(const char* fileName) : error(ERROR_NONE) {
ifs.open(fileName);
if (!ifs.is_open()) { error = ERROR_BADFILE; return; }
}
FileUtility fu = FileUtility("bob.txt");
if (fu.error != FileUtility::ERROR_NONE) return 1;
Это разумные решения.
4. Глобальное состояние ошибки.
Я не удивлюсь, если некоторые программисты ответят на это «неправильное представление» на это возможное решение, но правда в том, что многие чрезвычайно успешные и выдающиеся кодовые базы используют это решение для общения состояние ошибки. Возможно, лучшими примерами являются переменная errno
, используемая C стандартной библиотекой (хотя следует отметить, что errno
вид работ в сочетании с прямыми кодами возврата) и GetLastError()
, используемой API Windows C. Я полагаю, что некоторые могут утверждать, что это действительно «подход C», а исключения - это «подход на С ++», но опять же я избегаю исключений, таких как чума.
В качестве альтернативы многопоточность не является проблемой для этого решения, поскольку errno
и GetLastError()
используют как локальное, так и локальное состояние ошибки потока.
Мне нравится это решение наилучшим образом, потому что оно простое, чрезвычайно неинвазивное и может быть легко использовано повторно с помощью различных кодовых баз, при условии, конечно, что вы определяете структуру ошибок (в основном, локальную переменную потока и, возможно, макрос/глобальность ERROR_NONE
, см. ниже) в своей собственной библиотеке, и в этом случае ваш код получает согласованность, когда дело доходит до обработки ошибок.
Пример:
#define ERROR_NONE 0
thread_local int error = ERROR_NONE;
struct FileUtility {
static const int ERROR_BADFILE = 1;
ifstream ifs;
FileUtility(const char* fileName);
};
FileUtility::FileUtility(const char* fileName) {
ifs.open(fileName);
if (!ifs.is_open()) { error = ERROR_BADFILE; return; }
}
FileUtility fu = FileUtility("bob.txt");
if (error != ERROR_NONE) return 1;
Это решение, которое я рекомендовал бы; в противном случае я бы пошел с решением атрибута объекта.
http://en.cppreference.com/w/cpp/language/exceptions – chris
Если вы не хотите использовать исключение, вы можете сделать загруженную переменную внутри класса, которую вы можете проверить из основного. –