【问题标题】:"Undefined Symbols" when inheriting from stdexcept classes从 stdexcept 类继承时的“未定义符号”
【发布时间】:2011-01-28 09:04:41
【问题描述】:

这是<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);
};

为什么&lt;stdexcept&gt; 没有出现此错误?

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 提升为它自己的全局范围类,这似乎解决了问题。

【问题讨论】:

  • 愚蠢的问题,你是否真的在源文件中定义了rpn_expression_error 构造函数?您确定将定义限定为rpn_expression_error::rpn_expression_error 吗?相应的目标文件是否链接到您的可执行文件中?如果其中一项或多项不正确,就会出现这种错误。

标签: c++ debugging inheritance exception


【解决方案1】:

您捕获异常的方式存在问题。具体来说,考虑以下代码:

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);

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

【讨论】:

  • 虽然我确实忘记了#include &lt;string&gt;;(哦!),但我是using namespace std;。我添加了#include,什么也没有。我添加了std::,什么也没有。我把双下划线去掉了,什么也没有。
  • @Austin:好吧,我想是时候发布一段重现错误的最小代码了,我们都可以将其粘贴到我们的编辑器中并自己尝试。
  • @Austin:不要在头文件中使用using namespace std;,你会因此而感到头疼。 (可能永远不会,但这更有争议。)我将编辑我的答案,以展示你应该如何完整地做这件事。但就像 sbi 所说,我们需要更多代码才能确定。 (哎呀,投反对票:P)
  • 感谢using namespace std 的提示。不知道。
  • @Austin:没问题。所以也许我很傻,但问题解决了吗?您在上面给出的代码(完整代码)不应进行任何更改以使其成为全局范围。 (也就是说,它应该可以正常工作,我刚刚添加到我的答案中。)
【解决方案2】:

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

#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
};

此外,除非您的 RPN 表达式都是硬编码的,否则其中的错误将是 runtime_errors,而不是 logic_errors。

【讨论】:

  • 感谢有关运行时与逻辑错误的说明。我阅读文档的方式听起来像是logic_error 代表了人为错误,而runtime_error 代表了技术错误。
【解决方案3】:

如果那是您的代码,那么问题是(正如我建议和 Adrian 也说过的)您已经为 rpn_expression_error 类声明了一个构造函数,但您还没有定义它。尝试将此添加到您的代码中(在您的类声明下方):

rpn_expression_error::rpn_expression_error(const string& arg)
  : logic_error(arg)
{
}

【讨论】:

    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-20
      • 2021-05-12
      • 2014-12-06
      • 1970-01-01
      • 1970-01-01
      • 2020-06-06
      • 1970-01-01
      相关资源
      最近更新 更多