Форум программистов, компьютерный форум CyberForum.ru

Вызывается не тот конструктор - C++

Восстановить пароль Регистрация
 
Рейтинг: Рейтинг темы: голосов - 10, средняя оценка - 4.70
mdt::Vladimir
7 / 7 / 0
Регистрация: 23.09.2011
Сообщений: 32
15.03.2012, 15:13     Вызывается не тот конструктор #1
Имеется класс, вот часть его объявления:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MQLCopir : protected ClientCopir
{
private:
    bool _fatal;
    std::string _errMsg;
protected:
    void fatalError(bool fatal) {_fatal = fatal;}
    void errorMessage(const std::string &msg) {_errMsg = msg;}
public:
    MQLCopir(const char *login, const char *pass);
    MQLCopir(const std::string &login, const std::string &pass);
    MQLCopir(const std::string &msg, bool fatal = false);
    ~MQLCopir() {if (getTradeMode() != Error) deinit();}
    bool fatalError() const {return _fatal;}
    std::string errorMessage() const {return _errMsg;}
};
Как видно имеет 3 конструктора (хватило бы и двух, но из-за описанной ниже проблемы решил третий добавить с const char*). Ниже даны определения конструкторов:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MQLCopir::MQLCopir(const char *login, const char *pass) :
                ClientCopir(Trader)
{
    init(login, pass, false);
}
 
MQLCopir::MQLCopir(const std::string &login, const std::string &pass) :
        ClientCopir(Trader)
{
    init(login, pass, false);
}
 
MQLCopir::MQLCopir(const std::string &msg, bool fatal)
        : ClientCopir(Error), _fatal(fatal), _errMsg(msg)
{
 
}
И ниже функция, где вызывается конструктор:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
EXPORT(void*) init(const char *login, const char *pass)
{
    try
    {
        return static_cast<void*>(new MQLCopir(login, pass));
    }
    catch (Exception &e)
    {
        return static_cast<void*>(new MQLCopir(e.message(), true));
    }
    catch (Poco::Exception &e)
    {
        return static_cast<void*>(new MQLCopir(e.message(), true));
    }
    catch (std::exception &e)
    {
        return static_cast<void*>(new MQLCopir(e.what(), true));
    }
    return NULL;
}
Эта функция предназначена для использования в другом языке, поэтому происходит преобразование к типу void*. Два конструктора в классе собственно предназначены для инициализации класса и дальнейшей работы с ним, третий конструктор предназначен для сигнализации об ошибке, чтобы вызывающая программа при проверке fatalError не пыталась в дальнейшем использовать класс и вывела сообщение об ошибке.
Проблема состоит в том, что при вызове функции init в блоке try-catch вызывается конструктор MQLCopir(const std::string &msg, bool fatal) вместо нужного. Вопроса два: почему вызывается не тот конструктор и как это исправить?
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
15.03.2012, 15:13     Вызывается не тот конструктор
Посмотрите здесь:

C++ Не вызывается конструктор базового класса
C++ Почему не вызывается конструктор копирования?
C++ Наследование(Не вызывается конструктор)
C++ Почему вызывается не тот метод?
Когда вызывается конструктор копирования? C++
Почему не вызывается конструктор копии? C++
C++ При создании класса конструктор вызывается 2 раза, затем вызывается деструктор о_О
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
silent_1991
Эксперт C++
4945 / 3021 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
15.03.2012, 15:53     Вызывается не тот конструктор #2
Даже если в классе есть конструктор MQLCopir(const char *, const char *), всё равно вызывается MQLCopir(const std::string&, bool)?
Если имеется ввиду, что вызывается "не тот" конструктор, когда в классе есть только два - MQLCopir(const std::string&, const std::string&) и MQLCopit(const std::string&, bool), то проблема в том, что при перегрузке пользовательские преобразование (а преобразование из const char * в std::string посредством её конструктора является именно пользовательским преобразованием) рассматриваются в последнюю очередь. Если мы прикинемся компилятором, то при вызове MQLCopir(const_char_ptr1, const_char_ptr2) можно расставить уровень "плохости" преобразований так (при этом 0 - преобразование не требуется, 1 - повышение, 2 - встроенное приведение, 3 - определённое пользователем приведение):
MQLCopir(const std::string&, const std::string&): 3 3
MQLCopir(const std::string&, bool): 3 2
Понятно, что компилятор выберет второй вариант, потому что он лучше в отношении "плохости" преобразований.
mdt::Vladimir
7 / 7 / 0
Регистрация: 23.09.2011
Сообщений: 32
15.03.2012, 16:08  [ТС]     Вызывается не тот конструктор #3
Даже при наличии конструктора MQLCopir(const char *, const char *) вызывается MQLCopir(const std::string&, bool). То, что компилятору легче сделать преобразование const char* в bool, чем в std::string, это я тоже догадывался, поэтому и добавил третий конструктор, в котором преобразовывать вообще ничего не надо.
CheshireCat
Эксперт С++
2907 / 1235 / 78
Регистрация: 27.05.2008
Сообщений: 3,315
15.03.2012, 16:13     Вызывается не тот конструктор #4
1. Пример кода, приводящего к такому вызову, привести можешь?
2. Какой компилятор?
silent_1991
Эксперт C++
4945 / 3021 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
15.03.2012, 16:16     Вызывается не тот конструктор #5
mdt::Vladimir, http://liveworkspace.org/code/0d175d...b2e794577c35a9
mdt::Vladimir
7 / 7 / 0
Регистрация: 23.09.2011
Сообщений: 32
15.03.2012, 17:27  [ТС]     Вызывается не тот конструктор #6
Вообще этот код предназначен для работы на Windows, но разрабатывается и тестируется под Linux, т.к. это часть большого проекта для freeBSD, а это кусок клиентской части, компилятор - GCC 4.4.3.
Пример кода, при водящего к такому вызову:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "unittest++/UnitTest++.h"
#include "cwrapper.h"
#include "logger.h"
 
TEST(cwrapper)
{
    Priority pr = logger->getLevel();
    logger->setLevel(PRIO_SUBTRACE);
    void* copir = init("test", "123");
    CHECK(!fatal_error(copir));
    if (fatal_error(copir))
    {
        const int bufsize = 1024;
        char buf[bufsize];
        error_message(copir, buf, bufsize);
        logger->fatal("fatal error: %s", buf);
    }
    deinit(copir);
    logger->setLevel(pr);
}
выводит это (первая строка - это из библиотеки unittest++, вторую формирует "logger"):
test/test_cwrapper.cpp:19: error: Failure in cwrapper: !fatal_error(copir)
12-03-15 17:47:16 [FATAL] fatal error: test
Недостающие функции:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define COPIR_CAST(arg) static_cast<MQLCopir*>(arg)
 
EXPORT(void) deinit(void *copir)
{
    delete COPIR_CAST(copir);
}
 
EXPORT(bool) fatal_error(void *copir)
{
    if (copir == NULL)
        return true;
    return COPIR_CAST(copir)->fatalError();
}
 
EXPORT(int) error_message(void *copir, char *buff, int buffsize)
{
    std::string msg = (copir ? (COPIR_CAST(copir)->errorMessage()) : "Fatal error");
    strncpy(buff, msg.c_str(), buffsize);
    return strlen(buff);
}
Добавлено через 57 минут
CheshireCat, зачем про пример и компилятор-то спрашивал?
CheshireCat
Эксперт С++
2907 / 1235 / 78
Регистрация: 27.05.2008
Сообщений: 3,315
15.03.2012, 17:48     Вызывается не тот конструктор #7
Я попытался воспроизвести твою проблему.... короче, у тебя в коде, судя по всему, зацикливание: функция init("test", "123") зовет конструктор MQLCopir, а тот, в свою очередь, зовет опять init().... да, и там еще разночтение: то init(const char*, const char*), то init(const char*, const char*, bool) - где правда? или в листинге отсутствует еще одна функция?

В общем, если попытаться "предположить, как оно должно работать" и добиться нормальной работы - но не факт, что правильно, т.к. телепатии я не обучен :-), то результат у меня ровно такой же, как и у коллеги silent_1991.
Компиль GCC 4.7.0.
mdt::Vladimir
7 / 7 / 0
Регистрация: 23.09.2011
Сообщений: 32
15.03.2012, 20:03  [ТС]     Вызывается не тот конструктор #8
Аа, ну да... Я и не заметил, что там тоже init )) Это метод класса MQLCopir, т.е. там нет рекурсии. Я просто не целиком объявление класса выложил. На всякий случай, вот полностью:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class MQLCopir : protected ClientCopir
{
private:
    bool _fatal;
    std::string _errMsg;
protected:
    void fatalError(bool fatal) {_fatal = fatal;}
    void errorMessage(const std::string &msg) {_errMsg = msg;}
public:
    MQLCopir(const char *login, const char *pass);
    MQLCopir(const std::string &login, const std::string &pass);
    MQLCopir(const std::string &msg, bool fatal = false);
    ~MQLCopir() {if (getTradeMode() != Error) deinit();}
    bool fatalError() const {return _fatal;}
    std::string errorMessage() const {return _errMsg;}
    void clearOrders() {clientOrders.clear();}
    void addOrder(const MQLOrder &order) {clientOrders.push_back(order);}
    void sendOrders();
protected:
    using ClientCopir::addOrder;
    void updateOrders(int ID, const OrderList &ol);
private:
    void init(const std::string &login, const std::string &pass, bool toStart = true);
    void deinit();
    Poco::Mutex orderMutex;
    typedef std::map<int, OrderList> AllOrders;
    AllOrders serverOrders;
    MQLOrderList clientOrders;
};
А то, что вызывается неправильный конструктор, проверял в отладчике пошагово. Ну и программа выдаёт на косоль сообщение об ошибке, которое соответствует тому, что я ввожу в login, т.е. в этом плане всё сходится и 100%, что вызывается конструктор MQLCopir(std::string&, bool). Тут всё-таки можно предположить, что возникает исключение, но это не так.

Добавлено через 5 минут
Может ли быть подобное из-за обращения по неверному указателю или из-за переполнения буфера где-нибудь в программе? Я знаю, что бывают невероятные глюки из-за такого, но обычно всё отваливается после этого, поэтому как-то такой вариант не рассматриваю, тем более, что тут просто тест и в нём мало что происходит, чтоб такие ошибки возникали.

Добавлено через 27 минут
Всё, разобрался... Я даже к конструктору MQLCopir(const std::string&, bool) добавил ещё третий параметр, чтобы уж точно он не мог быть вызван, НО каким-то фантастическим образом вызывался. В итоге обратил внимание, что исполняемый файл у меня позавчерашний (тот, что тест содержит), сам же тестируемый код собирается в библиотеку, которая имеет нормальную дату модификации. Короче проблема в том, что из-за чего-то не собирался этот бинарник, но библиотека собиралась, ну и как-то этот тест вызывал другой конструктор. Вот такая бредовая ситуация... А я уж думал, что у меня компилятор глючит.

Добавлено через 4 минуты
Хотя нет, всё равно бред какой-то, ведь в тесте конструктор не вызывается, а вызывается функция из библиотеки, которая уже вызывает конструктор, и которая актуальная.
silent_1991
Эксперт C++
4945 / 3021 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
15.03.2012, 20:15     Вызывается не тот конструктор #9
Так, я окончательно запутался. Попробуйте привести минимальный код, воспроизводящий проблему. Без дополнительных методов, без хитроумных названий классов, без левых библиотек. Просто автономный код, достаточный для того, чтобы понять, что делается в реальном коде и чтобы увидеть аналогичное ему поведение.
mdt::Vladimir
7 / 7 / 0
Регистрация: 23.09.2011
Сообщений: 32
16.03.2012, 09:38  [ТС]     Вызывается не тот конструктор #10
Нет, минимальный код привести не могу, видимо, произошло это случайно. Как именно - сам не понимаю. Есть библиотека, в ней класс, и есть исполняемый файл, который создаёт объект этого класса. Исполняемый файл был слинкован со старой версией библиотеки, где был один конструктор класса, и этот класс вроде бы имел множественное наследие от двух классов. Потом исполняемый файл не менялся, а библиотека обновлялась, при этом класс уже не имел множественного наследия (имея тип void* сложно определить, на объект какого класса он указывает, чтобы привести к нужному типу), но имел несколько конструкторов.
По идее в этом случае должна была вылетать ошибка исполнения или вообще программа должна была не запуститься, потому что нужного класса уже нет в библиотеки, но почему-то происходила вот такая странная вещь.
silent_1991
Эксперт C++
4945 / 3021 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
16.03.2012, 12:44     Вызывается не тот конструктор #11
Т.е. теперь всё нормально?
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
16.03.2012, 14:45     Вызывается не тот конструктор
Еще ссылки по теме:

Конструктор вызывается 2 раза C++
C++ Не вызывается конструктор копии
Не вызывается конструктор C++
C++ Какой конструктор вызывается ?
Вызывается не тот конструктор C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
mdt::Vladimir
7 / 7 / 0
Регистрация: 23.09.2011
Сообщений: 32
16.03.2012, 14:45  [ТС]     Вызывается не тот конструктор #12
Да, теперь всё работает, спасибо, что попытались в моей проблеме разобраться. Хотя я так и не разобрался в том, почему это вообще происходило, даже воспроизвести не получается.
Yandex
Объявления
16.03.2012, 14:45     Вызывается не тот конструктор
Ответ Создать тему
Опции темы

Текущее время: 16:57. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru