从stdexcept类继承时的“未定义符号”

时间:2010-03-16 23:56:31

标签: c++ debugging inheritance exception

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

4 个答案:

答案 0 :(得分: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 :(得分:1)

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

答案 2 :(得分: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
};

此外,除非你的RPN表达式都是硬编码的,否则它们中的错误将是runtime_error s,而不是logic_error s。

答案 3 :(得分:0)

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

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