2015-04-05 3 views
2

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

FileUtility::FileUtility(const char *fileName) { 
    ifstream in_stream; 
    in_stream.open(filename); 
} 

FileUtility fu = FileUtility("bob.txt"); 
fu.read(); 
fu.write(); 

Файл bob.txt не существует, так что я не хочу, чтобы метод чтения и записи.

Есть ли чистый способ сделать это?

+2

http://en.cppreference.com/w/cpp/language/exceptions – chris

+0

Если вы не хотите использовать исключение, вы можете сделать загруженную переменную внутри класса, которую вы можете проверить из основного. –

ответ

1

Есть правило, четыре способа состояние ошибки может быть передано от вызываемого к абоненту:

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; 

Это решение, которое я рекомендовал бы; в противном случае я бы пошел с решением атрибута объекта.

4

Когда конструкция объекта не работает на C++, вы должны выбросить исключение или распространить исключение из неудачной конструкции подобъекта.

FileUtility(const char* filename) { 
    std::ifstream in_stream; 
    in_stream.exceptions(std::ios_base::failbit); 
    in_stream.open(filename); // will throw if file can't be opened 
} 

В вызывающем коде вы можете обработать исключение:

try { 
    FileUtility fu = FileUtility("bob.txt"); 
} catch (std::ios_base::failure) { 
    printf("Failed to open bob.txt\n"); 
    exit(EXIT_FAILURE); 
} 
// do other stuff 

Или, если вы не поймать исключение, среда выполнения будет просто позвонить std::terminate(), которая будет печатать свои собственные сообщение об ошибке, которое может или не может быть полезным:

terminate called after throwing an instance of 'std::ios_base::failure' 
    what(): basic_ios::clear 
Aborted 
Смежные вопросы