Привет!
Об этой стратегии обработки ошибок пользователя я прочитал в книге 
Professional C++ (2nd Edition) Скачать книгу (99МБ)+код к книге (464КБ) одним архивом (90МБ): 
https://yadi.sk/d/VCvsky11c6wtP Подробное описание создания собственных классов исключений находится здесь: "Chapter 10. Handling Errors" -> "Exceptions and Polymorphism" -> "Writing Your Own Exception Classes"
Проверка: непустой ли входной аргументФункция printArray принимает массив и выбрасывает исключение EmptyError, если входной аргумент пуст:
Output:
Error: empty argument in the function printArray()
main.cpp
C++ (Qt)
#include <vector>
#include <iostream>
#include "freeFunctions.h"
 
int main()
{
    std::vector<int> arr;
 
    try {
        printArray( arr );
    } catch ( const LogicError &e ) {
        std::cerr << e.what() << std::endl;
        return 1;
    } catch ( ... ) {
        std::cerr << "Error: unknown expection" << std::endl;
        return 1;
    }
 
    return 0;
}
 
freeFunctions.h
C++ (Qt)
#ifndef FREEFUNCTIONS_H
#define FREEFUNCTIONS_H
 
#include <vector>
#include <iostream>
#include "EmptyArgument.h"
 
void printArray( const std::vector<int> &arr )
throw ( EmptyArgument );
 
#endif // FREEFUNCTIONS_H
 
freeFunctions.cpp
C++ (Qt)
#include "freeFunctions.h"
 
void printArray( const std::vector<int> &arr )
    throw ( EmptyArgument )
{
    std::string functionName = "printArray()";
 
    if ( arr.empty( ) ) {
        throw EmptyArgument( functionName );
    }
 
    for ( std::size_t i = 0; i < arr.size(); ++i ) {
        std::cout << arr[i] << std::endl;
    }
}
 
EmptyArgument.h
C++ (Qt)
#ifndef EMPTYARGUMENT_H
#define EMPTYARGUMENT_H
 
#include <string>
#include "LogicError.h"
 
class EmptyArgument : public LogicError
{
public:
    EmptyArgument( const std::string &functionName ) :
        LogicError( functionName )
    {
        m_message = "Error: empty argument in the "
                "function " + m_functionName;
    }
};
 
#endif // EMPTYARGUMENT_H
 
LogicError.h
C++ (Qt)
#ifndef LOGICERROR_H
#define LOGICERROR_H
 
#include <string>
#include <stdexcept>
 
class LogicError : public std::logic_error
{
public:
 
    LogicError( const std::string &functionName ) :
        std::logic_error( "" ),
        m_functionName( functionName ),
        m_message( "" )
    {
 
    }
 
    virtual ~LogicError( ) throw( )
    {
 
    }
 
    virtual const char *what( ) const throw( )
    {
        return m_message.c_str( );
    }
 
    std::string message( ) const
    {
        return m_message;
    }
 
protected:
    std::string m_functionName;
    std::string m_message;
};
 
#endif // LOGICERROR_H
 
Проверки: открыт ли файл, удачна ли запись, удачно ли чтениеЕсли файл не открывается, то выбрасывается исключение FileOpenError с текстом, к примеру:
Error: unable to open the file "input.txt" in the "readData()"
Если из файла невозможно прочитать, то - FileReadError с тектом:
Error: unable to read the file "input.txt" in the "readData()"
Если диск переполнен и невозможно записать, то - FileWriteError с текстом:
Error: unable to write to the file "input.txt" in the "readData()"
Все выше перечисленные исключения наследуются от FileError, поэтому мы можем все три исключения отлавливать так:
C++ (Qt)
    try {
        // ...
    }
    catch ( const FileError &e ) {
        std::cerr << e.what( ) << std::endl;
        return 1;
    } catch ( ... ) {
        std::cerr << "Error: unknown exception" << std::endl;
        return 1;
    }
 
Исключение EmptyArgument выбрасывается, когда входные данные пустые и наследуется от LogicError
Следующий пример читает имена и фамилии из файла, заполняет массив объектов типа Person и выводит имена на экран:
main.cpp
C++ (Qt)
#include <iostream>
#include <vector>
#include <QString>
#include "freeFunctions.h"
#include "Person.h"
 
int main( )
{
    // Person array for saving
    Person david( "David", "White");
    Person ivan( "Ivan", "Green" );
    std::vector<Person> persons;
    persons.push_back( david );
    persons.push_back( ivan );
 
    try {
        // Parse the person array to the string content
        QString content;
        parsePersonsToStrContent( persons, content );
 
        // Save the string content to the file
        QString fileName = "file.txt";
        writeData( fileName, content );
 
        // Read the string content from the file
        QString readContent;
        readData( fileName, readContent );
 
        // Parse the string content to the person array
        std::vector<Person> readPersons;
        parseContentToPersons( readContent, readPersons );
 
        // Print the person array on the screen
        printData( readPersons );
    } catch ( const LogicError &e ) {
        std::cerr << e.what( ) << std::endl;
        return 1;
    } catch ( const FileError &e ) {
        std::cerr << e.what( ) << std::endl;
        return 1;
    } catch ( ... ) {
        std::cerr << "Error: unknown exception" << std::endl;
        return 1;
    }
 
    return 0;
}
 
Person.h
C++ (Qt)
#ifndef PERSON_H
#define PERSON_H
 
#include <QString>
 
class Person {
public:
 
    Person( const QString &firstName = "",
            const QString &lastName = "" ) :
        m_firstName( firstName ),
        m_lastName( lastName )
    {
 
    }
 
    QString firstName( ) const
    {
        return m_firstName;
    }
 
    QString lastName( ) const
    {
        return m_lastName;
    }
 
    void setFirstName( const QString &firstName )
    {
        m_firstName = firstName;
    }
 
    void setLastName( const QString &lastName )
    {
        m_lastName = lastName;
    }
 
private:
    QString m_firstName;
    QString m_lastName;
};
 
#endif // PERSON_H
 
freeFunctions.h
C++ (Qt)
#ifndef FREEFUNCTIONS_H
#define FREEFUNCTIONS_H
 
#include <vector>
 
#include <QString>
 
#include "FileOpenError.h"
#include "FileReadError.h"
#include "FileWriteError.h"
#include "EmptyArgument.h"
#include "Person.h"
 
void readData( const QString &fileName, QString &content )
throw ( EmptyArgument, FileOpenError, FileReadError );
 
void parseContentToPersons( const QString &content,
                            std::vector<Person> &persons )
throw ( EmptyArgument );
 
void parsePersonsToStrContent( const std::vector<Person> &persons,
                               QString &content)
throw ( EmptyArgument );
 
void writeData( const QString &fileName,
                const QString &content )
throw ( EmptyArgument, FileOpenError, FileWriteError );
 
void printData( const std::vector<Person> &persons )
throw ( EmptyArgument );
 
#endif // FREEFUNCTIONS_H
 
freeFunctions.cpp
C++ (Qt)
#include <iostream>
#include <string>
#include <QFile>
#include <QRegExp>
#include <QTextStream>
#include <QDebug>
#include "freeFunctions.h"
 
void readData(const QString &fileName, QString &content )
throw ( EmptyArgument, FileOpenError, FileReadError )
{
    std::string functionName = "readData()";
 
    // Check argument
    if ( fileName.isEmpty( ) ) {
        throw EmptyArgument( functionName );
    }
 
    // Open the input file for reading
    QFile file( fileName );
    if( !file.open( QIODevice::ReadOnly ) ) {
        throw FileOpenError( fileName.toStdString( ), functionName );
    }
 
    // Read the content from the file
    QByteArray data = file.readAll( );
    if ( data.isEmpty( ) ) {
        throw FileReadError( fileName.toStdString( ), functionName );
    }
 
    content = QString( data );
}
 
void parseContentToPersons( const QString &content, std::vector<Person> &persons )
throw ( EmptyArgument )
{
    std::string functionName = "parseContentToPersons()";
 
    // Check the input argument
    if ( content.isEmpty( ) ) {
        throw EmptyArgument( functionName );
    }
 
    QRegExp regExp("(\\w+) (\\w+)");
    int pos = 0;
    while ( ( pos = regExp.indexIn( content, pos ) ) != -1 ) {
        QString firstName = regExp.cap( 1 );
        QString lastName = regExp.cap( 2 );
        Person person( firstName, lastName );
        persons.push_back( person );
        pos += regExp.matchedLength( );
    }
}
 
void parsePersonsToStrContent( const std::vector<Person> &persons,
                               QString &content)
throw ( EmptyArgument )
{
    std::string functionName = "parsePersonsToStrContent()";
 
    // Check the input argument
    if ( persons.empty( ) ) {
        throw EmptyArgument( functionName );
    }
 
    for ( std::size_t i = 0; i < persons.size( ); ++i ) {
        QString firstName = persons[i].firstName( );
        QString lastName = persons[i].lastName( );
        QString line = QString( "%1 %2\n" ).arg( firstName ).arg( lastName );
        content.append( line );
    }
}
 
void writeData( const QString &fileName, const QString &content )
throw ( EmptyArgument, FileOpenError, FileWriteError )
{
    std::string functionName = "writeData()";
 
    // Check arguments
    if ( fileName.isEmpty( ) || content.isEmpty( ) ) {
        throw EmptyArgument( functionName );
    }
 
    // Open the output file for writing
    QFile file( fileName );
    if ( !( file.open( QIODevice::WriteOnly ) ) ) {
        throw FileOpenError( fileName.toStdString( ), functionName );
    }
 
    // Write data to the output file
    QTextStream stream( &file );
    stream << content;
    if ( stream.status() != QTextStream::Ok ) {
        throw FileWriteError( fileName.toStdString( ), functionName );
    }
}
 
void printData( const std::vector<Person> &persons )
throw ( EmptyArgument )
{
    std::string functionName = "printData()";
 
    // Check the input argument
    if ( persons.empty( ) ) {
        throw EmptyArgument( functionName );
    }
 
    // Print data
    for ( std::size_t i = 0; i < persons.size( ); ++i ) {
        std::cout << "First Name: " << persons[i].firstName( ).toStdString( ) << std::endl;
        std::cout << "Last Name: " << persons[i].lastName( ).toStdString( ) << std::endl;
        std::cout << std::endl;
    }
}
 
FileError.h
C++ (Qt)
#ifndef FILEERROR_H
#define FILEERROR_H
 
#include <string>
#include <stdexcept>
 
class FileError : public std::runtime_error
{
public:
 
    FileError( const std::string &fileName,
               const std::string &functionName) :
        std::runtime_error( "" ),
        m_message( "" ),
        m_fileName( fileName ),
        m_functionName( functionName )
    {
 
    }
 
    virtual ~FileError( ) throw( )
    {
 
    }
 
    virtual const char *what() const throw( )
    {
        return m_message.c_str( );
    }
 
    std::string message( ) const
    {
        return m_message;
    }
 
protected:
    std::string m_message;
    std::string m_fileName;
    std::string m_functionName;
};
 
#endif // FILEERROR_H
 
FileOpenError.h
C++ (Qt)
#ifndef FILEOPENERROR_H
#define FILEOPENERROR_H
 
#include <string>
#include "FileError.h"
 
class FileOpenError : public FileError {
public:
 
    FileOpenError( const std::string &fileName,
                   const std::string &functionName) :
        FileError( fileName, functionName )
    {
        m_message = "Error: unable to open the file \"" +
                m_fileName + "\" in the function \"" +
                m_functionName + "\"";
    }
};
 
#endif // FILEOPENERROR_H
 
FileReadError.h
C++ (Qt)
#ifndef FILEREADERROR_H
#define FILEREADERROR_H
 
#include <string>
#include "FileError.h"
 
class FileReadError : public FileError {
public:
 
    FileReadError( const std::string &fileName,
                   const std::string &functionName ) :
        FileError( fileName, functionName )
    {
        m_message = "Error: unable to read the file \"" + m_fileName +
                "\" in the function \"" + m_functionName + "\"";
    }
};
 
#endif // FILEREADERROR_H
 
FileWriteError.h
C++ (Qt)
#ifndef FILEWRITEERROR_H
#define FILEWRITEERROR_H
 
#include <string>
#include "FileError.h"
 
class FileWriteError : public FileError {
public:
 
    FileWriteError( const std::string &fileName,
                    const std::string &functionName ) :
        FileError( fileName, functionName )
    {
        m_message = "Error: unable to write to the file " +
                m_fileName + " in the function " + m_functionName;
    }
};
 
#endif // FILEWRITEERROR_H
 
EmptyArgument.h
C++ (Qt)
#ifndef EMPTYARGUMENT_H
#define EMPTYARGUMENT_H
 
#include <string>
#include "LogicError.h"
 
class EmptyArgument : public LogicError {
public:
 
    EmptyArgument( const std::string &functionName ) :
        LogicError( functionName )
    {
        m_message = "Error: empty argument in the "
                "function " + m_functionName;
    }
};
 
#endif // EMPTYARGUMENT_H
 
LogicError.h
C++ (Qt)
#ifndef LOGICERROR_H
#define LOGICERROR_H
 
#include <string>
#include <stdexcept>
 
class LogicError : public std::logic_error
{
public:
 
    LogicError( const std::string &functionName ) :
        std::logic_error( "" ),
        m_functionName( functionName ),
        m_message( "" )
    {
 
    }
 
    virtual ~LogicError( ) throw( )
    {
 
    }
 
    virtual const char *what( ) const throw( )
    {
        return m_message.c_str( );
    }
 
    std::string message( ) const
    {
        return m_message;
    }
 
protected:
    std::string m_functionName;
    std::string m_message;
};
 
#endif // LOGICERROR_H
 
Проверка деления на ноль и выхода из диапазонаВ моём примере есть функция divide(a, b), где a и b должны быть в диапазоне [-1000, 1000]. Если один из параметров превысил этот диапазон, то функция выбросит исключение с текстом:
Error: values must be from the range [-1000, 1000] in the function Calculator::divide()
Допустим в моём проекте 10 классов, и в каждой по несколько функций, которые должны следить за диапазоном своих аргументов. Мне при написании этих функций достаточно выбросить после проверки исключение:
C++ (Qt)
throw OutOfRange<int>( beginOfRange, endOfRange, functionName );
main.cpp
C++ (Qt)
#include <iostream>
#include "Calculator.h"
 
int main()
{
    Calculator<float> calculator;
 
    try {
        float result = calculator.divide( 24.7f, 3.0f );
        std::cout << "Result = " << result << std::endl;
    } catch ( const LogicError &e ) {
        std::cerr << e.what() << std::endl;
        return 1;
    } catch ( ... ) {
        std::cerr << "Error: unknown expection" << std::endl;
        return 1;
    }
 
    return 0;
}
Calculator.h
C++ (Qt)
#ifndef CALCULATOR_H
#define CALCULATOR_H
 
#include <string>
#include "DivideByZero.h"
#include "OutOfRange.h"
 
template <typename Type>
class Calculator
{
public:
    // Divide nums from the range [-1000, 1000]
    Type divide( Type a, Type b )
    throw ( DivideByZero, OutOfRange<int> )
    {
        std::string functionName = "Calculator::divide()";
        if ( b == 0 ) {
            throw DivideByZero( functionName );
        }
 
        const int beginOfRange = -1000;
        const int endOfRange = 1000;
 
        if ( ( a < beginOfRange ) || ( a > endOfRange ) ||
             ( b < beginOfRange ) || ( b > endOfRange ) )
        {
            throw OutOfRange<int>( beginOfRange,
                                   endOfRange,
                                   functionName );
        }
 
        return a / b;
    }
};
 
#endif // CALCULATOR_H
DivideByZero.h
C++ (Qt)
#ifndef DIVIDEBYZERO_H
#define DIVIDEBYZERO_H
 
#include <string>
#include "LogicError.h"
 
class DivideByZero : public LogicError
{
public:
    DivideByZero( const std::string &functionName ) :
        LogicError( functionName )
    {
        m_message = "Error: divide by zero in the "
                "function " + m_functionName;
    }
};
 
#endif // DIVIDEBYZERO_H
OutOfRange.h
C++ (Qt)
#ifndef OUTOFRANGE_H
#define OUTOFRANGE_H
 
#include <string>
#include "LogicError.h"
 
template <typename Type>
class OutOfRange : public LogicError
{
public:
    OutOfRange( Type beginOfRange,
                Type endOfRange,
                const std::string &functionName ) :
        LogicError( functionName )
    {
        m_message = "Error: values must be from the range "
                    "[" + std::to_string( beginOfRange ) +
                    ", " + std::to_string( endOfRange ) + "]"
                    " in the function " + m_functionName;
    }
};
 
#endif // OUTOFRANGE_H
LogicError.h
C++ (Qt)
#ifndef LOGICERROR_H
#define LOGICERROR_H
 
#include <string>
#include <stdexcept>
 
class LogicError : public std::logic_error
{
public:
 
    LogicError( const std::string &functionName ) :
        std::logic_error( "" ),
        m_functionName( functionName ),
        m_message( "" )
    {
 
    }
 
    virtual ~LogicError( ) throw( )
    {
 
    }
 
    virtual const char *what( ) const throw( )
    {
        return m_message.c_str( );
    }
 
    std::string message( ) const
    {
        return m_message;
    }
 
protected:
    std::string m_functionName;
    std::string m_message;
};
 
#endif // LOGICERROR_H
Интересные ссылки:
Правило 8: Не позволяйте исключениям покидать деструкторыПравило 25: Подумайте о поддержке функции swap, не возбуждающей исключенийПравило 29: Стремитесь, чтобы программа была безопасна относительно исключенийException Safety | QtDoc 5.3