【问题标题】:Catch Multiple Custom Exceptions? - C++捕获多个自定义异常? - C++
【发布时间】:2011-01-31 13:25:56
【问题描述】:

我是第一个 C++ 编程课程的学生,我正在做一个项目,我们必须创建多个自定义异常类,然后在我们的一个事件处理程序中,使用 try/catch 块来处理适当的。

我的问题是:如何在try/catch 块中捕获我的多个自定义异常? GetMessage() 是我的异常类中的一个自定义方法,它以std::string 形式返回异常说明。下面我已经包含了我项目中的所有相关代码。

感谢您的帮助!

try/catch 块


    // This is in one of my event handlers, newEnd is a wxTextCtrl
try {
    first.ValidateData();
    newEndT = first.ComputeEndTime();
    *newEnd << newEndT;
}
catch (// don't know what do to here) {
    wxMessageBox(_(e.GetMessage()), 
                 _("Something Went Wrong!"),
                 wxOK | wxICON_INFORMATION, this);;
}

ValidateData() 方法


void Time::ValidateData()
{
    int startHours, startMins, endHours, endMins;

    startHours = startTime / MINUTES_TO_HOURS;
    startMins = startTime % MINUTES_TO_HOURS;
    endHours = endTime / MINUTES_TO_HOURS;
    endMins = endTime % MINUTES_TO_HOURS;

    if (!(startHours <= HOURS_MAX && startHours >= HOURS_MIN))
        throw new HourOutOfRangeException("Beginning Time Hour Out of Range!");
    if (!(endHours <= HOURS_MAX && endHours >= HOURS_MIN))
        throw new HourOutOfRangeException("Ending Time Hour Out of Range!");
    if (!(startMins <= MINUTE_MAX && startMins >= MINUTE_MIN))
        throw new MinuteOutOfRangeException("Starting Time Minute Out of    Range!");
    if (!(endMins <= MINUTE_MAX && endMins >= MINUTE_MIN))
        throw new MinuteOutOfRangeException("Ending Time Minute Out of Range!");
    if(!(timeDifference <= P_MAX && timeDifference >= P_MIN))
        throw new PercentageOutOfRangeException("Percentage Change Out of Range!");
    if (!(startTime < endTime))
        throw new StartEndException("Start Time Cannot Be Less Than End Time!");
}

只是我的自定义异常类之一,其他的与这个具有相同的结构


class HourOutOfRangeException
{
public:
        // param constructor
        // initializes message to passed paramater
        // preconditions - param will be a string
        // postconditions - message will be initialized
        // params a string
        // no return type
        HourOutOfRangeException(string pMessage) : message(pMessage) {}
        // GetMessage is getter for var message
        // params none
        // preconditions - none
        // postconditions - none
        // returns string
        string GetMessage() { return message; }
        // destructor
        ~HourOutOfRangeException() {}
private:
        string message;
};

【问题讨论】:

  • 不要抛出指针,省略新的。
  • 看到你从现在到现在的进步真是太棒了,你是一个拥有超过 40k 代表的顶级 SO 用户。真的太棒了!

标签: c++ try-catch custom-exceptions


【解决方案1】:

如果您有多种异常类型,并假设有一个异常层次结构(并且所有异常都从std::exception 的某个子类公开派生),则从最具体的开始并继续更一般:

try
{
    // throws something
}
catch ( const MostSpecificException& e )
{
    // handle custom exception
}
catch ( const LessSpecificException& e )
{
    // handle custom exception
}
catch ( const std::exception& e )
{
    // standard exceptions
}
catch ( ... )
{
    // everything else
}

另一方面,如果您只对错误消息感兴趣 - throw 相同的异常,请说 std::runtime_error 带有不同的消息,然后 catch 表示:

try
{
    // code throws some subclass of std::exception
}
catch ( const std::exception& e )
{
    std::cerr << "ERROR: " << e.what() << std::endl;
}

还要记住 - 按值抛出,按 [const] 引用捕获。

【讨论】:

    【解决方案2】:

    您应该创建一个基异常类,并让您的所有特定异常都派生自它:

    class BaseException { };
    class HourOutOfRangeException : public BaseException { };
    class MinuteOutOfRangeException : public BaseException { };
    

    然后您可以在一个 catch 块中捕获所有这些:

    catch (const BaseException& e) { }
    

    如果您希望能够拨打GetMessage,您需要:

    • 将该逻辑放入BaseException,或
    • 使GetMessage 成为BaseException 中的虚拟成员函数,并在每个派生异常类中覆盖它。

    您还可以考虑让您的异常派生自标准库异常之一,例如std::runtime_error,并使用惯用的what() 成员函数而不是GetMessage()

    【讨论】:

    • @Alex:我不确定这是不是很好的建议。在一般情况下,您无法控制异常的定义(例如,它们来自某个库,例如 Boost)。
    • 如果我抛出一个 int 怎么办?还是浮子?我应该将它们包装在 Int 或 Float 类中,然后从它们派生吗?
    【解决方案3】:

    如果模板不能,宏可以节省时间。 解决方案取自Boost。它归结为 7 行代码。

    /// @file multicatch.hpp
    #include <boost/preprocessor/variadic/to_list.hpp>
    #include <boost/preprocessor/list/for_each.hpp>
    
    /// Callers must define CATCH_BODY(err) to handle the error,
    /// they can redefine the CATCH itself, but it is not as convenient. 
    #define CATCH(R, _, T) \
      catch (T & err) {    \
        CATCH_BODY(err)    \
      }
    /// Generates catches for multiple exception types
    /// with the same error handling body.
    #define MULTICATCH(...) \
      BOOST_PP_LIST_FOR_EACH(CATCH, _, BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__))
    // end of file multicatch.hpp
    
    /// @file app.cc
    #include "multicatch.hpp"
    
    // Contrived example.
    /// Supply the error handling logic.
    #define CATCH_BODY(err)                        \
      log() << "External failure: " << err.what(); \
      throw;
    
    void foo() {
      try {
        bar();  // May throw three or more sibling or unrelated exceptions.
      }
      MULTICATCH(IOError, OutOfMemory)
    }
    
    #undef CATCH_BODY
    

    【讨论】:

      【解决方案4】:

      从具有虚拟方法 GetMessage() 的公共基类 BaseException 派生所有异常。

      然后catch(const BaseException&amp; e)

      【讨论】:

        【解决方案5】:

        我今天遇到了类似的问题,但事实证明我不需要我的解决方案来解决我的问题。老实说,我想不出真正的用例(日志记录?),而且我在我的代码中没有发现它有多大用处。

        无论如何,这是一种使用类型列表的方法(需要 C++11)。我认为这种方法的优点是不需要为自定义异常提供通用基类(可能除了 std::exception 吗?)。换句话说,它不会侵入您的异常层次结构。

        可能有一些我不知道的细微错误。

        #include <type_traits>
        #include <exception>
        
        /// Helper class to handle multiple specific exception types
        /// in cases when inheritance based approach would catch exceptions
        /// that are not meant to be caught.
        ///
        /// If the body of exception handling code is the same
        /// for several exceptions,
        /// these exceptions can be joined into one catch.
        ///
        /// Only message data of the caught exception is provided.
        ///
        /// @tparam T  Exception types.
        /// @tparam Ts  At least one more exception type is required.
        template <class T, class... Ts>
        class MultiCatch;
        
        /// Terminal case that holds the message.
        /// ``void`` needs to be given as terminal explicitly.
        template <>
        class MultiCatch<void> {
         protected:
          explicit MultiCatch(const char* err_msg) : msg(err_msg) {}
          const char* msg;
        };
        
        template <class T, class... Ts>
        class MultiCatch : public MultiCatch<Ts...> {
          static_assert(std::is_base_of<std::exception, T>::value, "Not an exception");
        
         public:
          using MultiCatch<Ts...>::MultiCatch;
        
          /// Implicit conversion from the guest exception.
          MultiCatch(const T& error) : MultiCatch<Ts...>(error.what()) {}  // NOLINT
        
          /// @returns The message of the original exception.
          const char* what() const noexcept {
            return MultiCatch<void>::msg;
          }
        };
        
        /// To avoid explicit ``void`` in the type list.
        template <class... Ts>
        using OneOf = MultiCatch<Ts..., void>;
        
        /// Contrived example.
        void foo() {
          try {
            bar();  // May throw three or more sibling or unrelated exceptions.
          } catch (const OneOf<IOError, OutOfMemory>& err) {
            log() << "External failure: " << err.what();
        
            throw;  // Throw the original exception.
          }
        }
        

        【讨论】:

        • 代码不起作用。我没有注意测试。这只是一厢情愿。 catch 中不考虑隐式转换。
        【解决方案6】:

        我遇到了同样的问题,结果如下:

          std::shared_ptr<MappedImage> MappedImage::get(const std::string & image_dir,
                                                        const std::string & name,
                                                        const Packet::Checksum & checksum) {
            try {
              return std::shared_ptr<MappedImage>(images_.at(checksum));
            } catch (std::out_of_range) {
            } catch (std::bad_weak_ptr) {
            }
            std::shared_ptr<MappedImage> img =
              std::make_shared<MappedImage>(image_dir, name, checksum);
            images_[checksum_] = img;
            return img;
          }
        

        在我的情况下,函数在没有异常时返回。所以我实际上不必在 catch 内做任何事情,但可以在 try 之外做任何工作。

        【讨论】:

          【解决方案7】:

          当您无法控制异常的类层次结构并且无法复制 catch 块的内容时,解决此问题的另一种方法是使用 dynamic_cast 像这样:

          try
          {
             ...
          }
          catch (std::exception& e)
          {
              if(   nullptr == dynamic_cast<exception_type_1*> (&e)
                 && nullptr == dynamic_cast<exception_type_2*> (&e))
              {
                  throw;
              }
              // here you process the expected exception types
          }
          

          【讨论】:

            【解决方案8】:

            #include <iostream> void test(int x)` { try{ if(x==1) throw (1); else if(x==2) throw (2.0); } catch(int a) { cout<<"It's Integer"; } catch(double b) { cout<<"it's Double"; } } int main(){ cout<<" x=1"; test(1); cout<<"X=2"; test(2.0); return 0; }`

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-09-22
              • 1970-01-01
              • 2017-05-24
              • 2020-04-03
              • 2021-03-18
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多