«Неопределенные символы» при наследовании от классов stdexcept - PullRequest
4 голосов
/ 17 марта 2010

Вот исключение, определенное в <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 до своего собственного класса global-scope, и это, похоже, решило проблему.

Ответы [ 4 ]

3 голосов
/ 17 марта 2010

Существует проблема с тем, как вы ловите исключения. В частности, рассмотрим этот код:

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 голос
/ 17 марта 2010

В нем говорится, что функция не определена, потому что вы забыли ее определить. Попробуйте это:

#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 с, а не logic_error с.

1 голос
/ 17 марта 2010

Похоже, вы объявили конструктор, но не предоставили его определение.

0 голосов
/ 17 марта 2010

Если это ваш код, то проблема в том, что (как я уже говорил, и Адриан также сказал), вы объявили конструктор для своего rpn_expression_error класса, но не определили его. Попробуйте добавить это в свой код (под объявлением класса):

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