【问题标题】:Throwing primitive types in C++在 C++ 中抛出原始类型
【发布时间】:2016-05-20 19:22:18
【问题描述】:

我正在处理一个向用户请求文件、用户名和密码的对话框。我希望在用户的选择出现问题时通知用户。在我检查对话框的名称和密码字段之前,该文件必须有效。我知道还有其他方法可以做到这一点,我的问题是关于这个过程的效率。

我的 MFC C++ 应用程序有一个字符串表,其中使用将翻译单元映射到整数 (#define IDS_SOME_ERROR 100) 的宏来标识字符串,MFC 框架可以使用这些整数从字符串表中加载字符串.

我很好奇抛出原始类型的效率,所以我查看了cpprefrence.com 上的throw 关键字。

throw expression 语句从表达式复制初始化异常对象(这可能会调用右值表达式的移动构造函数,并且复制/移动可能会受到复制省略),然后将控制权转移到匹配的异常处理程序其复合语句或成员初始值设定项列表是最近进入该执行线程但未退出的类型。即使复制初始化选择移动构造函数,从左值复制初始化也必须格式正确,并且析构函数必须是可访问的(C++14 起)。

这是 another question 询问要抛出的类型,我注意到一条评论说不要抛出 intstring 之类的东西。这是为什么呢?

这是我的代码:

void SomeDialog::OnOK()
{   
    UpdateData();

    try
    {
        if (VerifySomeFile(myFileStruct.getPath()))
        {
            if (DialogNameField.IsEmpty())
            {
                throw IDS_ERROR_NOID; // "No name specified."
            }

            if (DialogPasswordField.IsEmpty())
            {
                throw IDS_ERROR_NOPWD; // "No password specified."
            }

            CDialog::OnOK();
        }
        else
        {
            throw IDS_BAD_FILE; // "Bad or expired file."
        }
    }
    catch (int errIDS)
    { 
        CString tempStr;
        tempStr.LoadString(errIDS);
        AfxMessageBox(tempStr, MB_OK);
    }
}

既然int 是C++ 中的原始类型,那么throw 语句是否还必须执行任何复制?以这种方式在 C++ 中抛出原始类型是否安全?删除 catch 块并将我的错误对话框逻辑从 catch 块放置到每个抛出位置会更有效吗?如果我要这样做,我还必须嵌套我的代码,以便在出现多个错误时只显示一个对话框。

【问题讨论】:

  • “为什么会这样?” - 正如公认的答案所说:“不要抛出原始类型,因为它们不携带语义信息。”
  • @ChristianHackl,但就我而言,我不太担心异常包含的信息,而更多地担心控制流。我觉得有一个创建对话框的控制点比在我的更复杂的源代码中创建对话框更好。

标签: c++ exception exception-handling try-catch


【解决方案1】:

您不必太担心效率,因为捕获std::exeption 派生项的推荐做法是使用const 引用。

它的优点是引入异常层次结构,并使用const char* std::exception::what() const 提供覆盖的错误消息。

【讨论】:

  • 根据定义,抛出异常也是不应该经常发生的事情,因此抛出本身的性能影响在很大程度上是无关紧要的。
【解决方案2】:

我会说做这样的事情而不是抛出内置类型会更好:

struct SomeError{int errid};
...
throw SomeError{IDS_BAD_FILE};

【讨论】:

  • 如果你认为这样更好,你应该能够解释原因。
  • 取决于捕获异常的位置,我认为最好抛出独立的结构(只是为了名称和捕获(结构))而不是内置类型(这里是int),因为其他东西也可能抛出具有相同值的 int。
  • 现在这是一个很好的答案 - 把理由放进去!
【解决方案3】:

让我们从正确使用术语开始。 C++ 没有原始类型的概念。在 C++ 中,有内置类型和自定义类型。

int,就此而言,是一个内置类型。虽然从语言的角度来看,抛出 int 类型的对象与抛出 MySpecialClass 类型的对象或 std::exception 的派生没有什么不同,但抛出后者有一个好处——因为这是一个众所周知的例外,人们可能会期望在程序中的某个地方会有catch (const std::exception& e)。此捕获将处理std::exception 的所有派生,并且至少可以在异常中打印消息。

然而,指望一个人抓住int 是不合理的!即使有,下一步呢?这个异常应该如何处理?数字打印到控制台?用户应该如何看待它?

【讨论】:

  • 我想我理解在大多数情况下抛出std::exception 衍生品的原因。但在这里我更关心控制流,我“滥用”了 try-catch 块,因为这意味着我的消息框只需要一个调用站点,并且主要对 try--throw- 的开销感到好奇catch 块添加到我的程序中。
  • @ZevIsert,性能开销相当大。堆栈展开是一项昂贵的操作。就此而言,当性能很重要时,不建议使用异常作为控制流。
  • 好点.. 但是我在范围内捕获它的情况呢?我确信在 try 块中调用的函数都不会抛出整数。 (著名的遗言)这里不应该有任何堆栈展开吗?还是程序必须从if 块中解脱出来?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-04
  • 2011-08-05
  • 2014-12-01
  • 1970-01-01
  • 2023-01-23
  • 1970-01-01
相关资源
最近更新 更多