从 stdexcept 类继承时出现“未定义的符号”错误

4

这里是在 <stdexcept> 中定义的一个异常:

class length_error : public logic_error 
{
public:
    explicit length_error(const string&  __arg);
};

这是我的异常:

#include <string>
#include <stdexcept>
using namespace std;

class rpn_expression_error : public logic_error
{
public:
    explicit rpn_expression_error(const string& __arg);
};

为什么当没有使用<stdexcept>时会出现这个错误?
Undefined symbols:
  rpn_expression_error::rpn_expression_error(/*string*/ const&), referenced from:
        ...
ld: symbol(s) not found

根据 @sbi 的要求,这是我目前代码的最小示例:

#include <string>
#include <iostream>
#include <stdexcept>
using namespace std;

class RPN_Calculator {
public:
    class rpn_expression_error : public logic_error {
    public:
        explicit rpn_expression_error(const string& arg) : logic_error(arg) {}
    };

    void Execute() {
        throw rpn_expression_error("Hello");
    }
};

int main() {
    RPN_Calculator calc;

    try {
        calc.Execute();
    } catch (exception e) {
        cout << e.what() << endl;
    }
}

我将其保存为rpn.cpp并运行make rpn以产生错误

现在代码已经完全建立,但是实际程序仍然给出原始错误。

注意/解决方案:虽然上面的代码运行得很好,但是在实际代码中相同的异常类仍然会产生链接器错误。为了简化起见,我只需将rpn_expression_error提升为自己的全局范围类,似乎问题已经解决了。


2
愚蠢的问题,你是否在源文件中定义了rpn_expression_error构造函数?并且你确定将定义限定为rpn_expression_error::rpn_expression_error吗?相应的目标文件是否被链接到可执行文件中?如果其中一个或多个条件不成立,那么这就是你会遇到的错误类型。 - Tyler McHenry
4个回答

3

你捕获异常的方式有问题。具体来说,请考虑以下代码:

struct Base
{
    virtual void do() { std::cout << "Base!" << std::endl; }
};

struct Derived : Base
{
    virtual void do() { std::cout << "Derived!" << std::endl; }
};

void foo(Base x)
{
    x.do();
}

int main()
{
    Derived d;
    foo(d); // <--
}

在那个标记的行上,d 被称为 "切片"。也就是说,为了满足成为 Base,所有不属于 Base 的部分都会被切掉!所以上面的代码将输出 "Base!"。
如果我们想要预期的输出,我们需要使参数不是一个值:
void foo(Base& x) // polymorphic
{
    x.do();
}

我们上面的代码将显示“Derived!”,因为它不再被切片。(也可以使用指针。)
因此,请查看您的catch子句:
catch (exception e)

在这里,你抛出的任何异常都会被“切片”成基本的std::exception类,丢失任何派生信息!这就是为什么更常见(可能也更“正确”)的做法是通过引用捕获异常:

catch (const exception& e)

现在你将会发现e.what()返回了预期的非切片错误信息。

旧版

以下是整个内容应该如何呈现(不要在头文件中使用using namespace!):

// rpn_expression_error.h
#include <stdexcept> // for logic_error
#include <string> // for string

class rpn_expression_error : public std::logic_error
{
public:
    explicit rpn_expression_error(const std::string& pMsg);
};

// rpn_expression_error.cpp
#include "rpn_expression_error.h"

rpn_expression_error::rpn_expression_error(const std::string& pMsg) :
std::logic_error(pMsg)
{}

旧版本

因为这些异常类是在标准命名空间中声明的,而你的不是。 string 在命名空间 std 中,所以它们不需要限定,但是你需要:

#include <string>

// ...
                                    vvv 
explicit rpn_expression_error(const std::string& arg);

请记住,我已更改您的参数名称。包含双下划线的名称是保留的,您不应该使用它们。


1
虽然我忘记了 #include <string>;(呃!),但我使用了 using namespace std;。我添加了 #include,但什么也没有发生。我添加了 std::,但仍然没有任何变化。我去掉了双下划线,但还是没有任何变化。 - Austin Hyde
2
@Austin:好的,我猜现在是时候你发布一个最小的代码片段来重现错误了,这样我们就可以全部把它粘贴到我们的编辑器中,尝试一下。 - sbi
3
@Austin: 不要在头文件中使用 using namespace std;,否则会引起各种麻烦。(或许永远不会有问题,但这更具争议性。)我将编辑我的答案以展示你应该如何完整地做这件事情。但像 sbi 所说的那样,我们需要更多的代码才能确定。(还有一颗痛苦的“踩下” :P) - GManNickG
感谢您提供“using namespace std”的提示,我之前并不知道。 - Austin Hyde
@Austin:没问题。也许我有点傻,但问题解决了吗?你上面给出的代码(完整代码)不应该有任何更改使其成为全局范围。(也就是说,它应该按原样正常工作,只需添加我在答案中刚刚添加的更改即可。) - GManNickG
@GMan:是的,现在一切都正常了。我已经删除了“using”,将“rpn_expression_error”从嵌套类提升(这不是问题,但简化了生活),为构造函数添加了定义和声明,并且现在通过引用捕获异常(我也不知道)。感谢您的所有帮助! - Austin Hyde

1

它说函数未定义,因为你忘记定义它了。试试这个:

#include <string>
#include <stdexcept>
using namespace std;

class rpn_expression_error : public logic_error
{
public:
    explicit rpn_expression_error(const string& arg)
      : logic_error( arg ) { } // definition
};

正如其他人所建议的那样,在头文件中使用using namespace是不好的实践,因此如果这是一个头文件,

#include <string>
#include <stdexcept>

class rpn_expression_error : public std::logic_error
{
public:
    explicit rpn_expression_error(const std::string& arg)
      : logic_error( arg ) { } // definition
};

此外,除非您的逆波兰表达式都是硬编码的,否则它们中的错误将是runtime_error而不是logic_error

感谢您提醒关于运行时错误和逻辑错误的问题。根据我阅读的文档,logic_error 似乎代表人为错误,而 runtime_error 则代表技术错误。 - Austin Hyde

1

看起来你已经声明了一个构造函数,但是没有提供它的定义。


0
如果这是你的代码,那么问题就在于你已经声明了一个rpn_expression_error类的构造函数,但你没有定义它(正如我所建议的和Adrian也说的)。尝试在你的代码中添加以下内容(在你的类声明下面):
rpn_expression_error::rpn_expression_error(const string& arg)
  : logic_error(arg)
{
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接