ошибка c bad_weak_ptr

#c #boost

#c #повышение

Вопрос:

Я хочу создать некоторый Timer класс, который печатает «текст» каждые N секунд, где N будет инициализировано в конструкторе.

 #include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <iostream>

class Timer : public boost::enable_shared_from_this<Timer>
{
public:
    Timer ( const double interval ) : interval_sec( interval )
    {
        io_service_ = new boost::asio::io_service;
        timer_      = new boost::asio::deadline_timer ( * io_service_ );
        start( );
        io_service_ -> run( );
    }

    void start ( )
    {
        timer_ -> expires_from_now( boost::posix_time::seconds( 0 ) );
        timer_ -> async_wait(boost::bind( amp;Timer::handler
                                        , shared_from_this( )
                                        , boost::asio::placeholders::error
                                        )
                            );
    }

private:
    void handler( const boost::system::error_codeamp; error )
    {
        if ( error )
        {
            std::cerr << error.message( ) << std::endl;
            return;
        }

        printf( "textn" );
        timer_ -> expires_from_now( boost::posix_time::seconds( interval_sec ) );
        timer_ -> async_wait( boost::bind( amp;Timer::handler
                                         , shared_from_this( )
                                         , boost::asio::placeholders::error
                                         )
                            );
    }

private:
    boost::asio::io_service * io_service_;
    boost::asio::deadline_timer * timer_;
    double interval_sec;
};

int main()
{
    boost::shared_ptr<Timer> timer( new Timer ( 10 ) );
    return 0;
}
  

Но у меня bad_weak_ptr ошибка.

 terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_weak_ptr> >'
  what():  tr1::bad_weak_ptr
Aborted
  

Что я делаю не так и как я могу это исправить?

Комментарии:

1. Вы прошлись по коду в своем отладчике? Я бы предположил, что это main() возвращается до срабатывания вашего таймера, что приводит timer к уничтожению. Вы уверены, что это async_wait относится к общему объекту?

2. Возможно, вы также захотите что-то сделать с утечками памяти. Создание элементов с new почти никогда не бывает хорошей идеей.

3. В этом вопросе немного не так, но это первый результат поиска в Google boost::bad_weak_ptr , поэтому я напишу это здесь. При попытке заменить также выдается приятное boost::bad_weak_ptr исключение ( terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_weak_ptr> > ) boost::shared_ptr<YourClass> на std::shared_ptr<YourClass> , так что будьте осторожны. 🙂

4. @Avio это очень хороший совет, который стал причиной моей аналогичной проблемы. Спасибо! В частности, не смешивайте использование std::make_shared с boost::weak_ptr в этом контексте.

Ответ №1:

Проблема, скорее всего, в том, что вы не можете использовать shared_from_this до тех пор, пока объект фактически не будет управляться общим указателем. В общем случае не рекомендуется запускать поток или асинхронную службу в конструкторе, так как вам может не повезти, и новый поток может начаться до завершения работы конструктора и, следовательно, выполняться на не полностью сконструированном объекте.

В вашем конкретном случае это еще хуже, поскольку вы входите в цикл событий внутри конструктора вашего Timer класса, и это означает, что управление никогда не возвращается к main , объект никогда не управляется shared_ptr в main…

Вы должны переместить вызов в start и вызов run() main другой функции и вызвать ее из после shared_ptr того, как объект фактически управляется в,,.

Комментарии:

1. На мой взгляд, это как-то некрасиво, что я не могу создать этот объект и забыть о нем. Я должен вызвать этот start( ) метод и так далее, и тому подобное. Кстати, могу ли я вызвать функцию io_service_ -> run( ); in start( ) ?

2. @garmOnboz1a: Нет проблем в том, чтобы создать объект и забыть о нем, просто не так, как вы это делаете: т. Е. есть проблема в вызове shared_from_this до того, как объект будет управляться в shared_ptr , и вызов run не завершится, а скорее захватит поток и выполнит цикл событий. Вы можете вызвать io_service_->run() откуда угодно, но он перехватит управление потоком и не вернется из него до тех пор, пока не будет завершен цикл управления. И, честно говоря, я не уверен, что имеет смысл делать это там.

3. С точки зрения дизайна не имеет смысла, что Timer будучи всего лишь одним из потенциальных классов (и объектов), которые могут быть зарегистрированы для асинхронных взаимодействий, захватывает поток в одном из его методов. Учтите, что если start это произойдет, то вы не сможете использовать более одного Timer потока — т. е. каждый таймер будет захватывать свой собственный поток… Вам следует прочитать руководство по асинхронному программированию и попытаться понять примеры и почему они построены таким образом.

4. Итак, если я хочу вызвать метод некоторого объекта (т. Е. из main), я должен создать один поток для io_service_->run() (тоже в main) и присоединиться к нему? Другими словами, как я могу вернуть управление в main() ?

5. В вашем коде есть единственный поток, и вы можете использовать его как вам заблагорассудится, но io_service_->run() он будет перехватываться. Это означает, что если вы хотите, чтобы была выполнена инструкция, она io_service_->run() не может быть вызвана перед этой инструкцией.

Ответ №2:

Перед вызовом shared_from_this() ваш класс должен быть сохранен в shared_ptr . Это означает, что вы не можете вызвать shared_from_this() внутри конструктора, потому что строка объекта не будет помещена в shared_ptr до завершения работы конструктора.

Это причина того, что классы, которые используют, enable_shared_from_this обычно имеют start функцию, которая выполняет заключительные шаги инициализации, требующие использования shared_from_this() . Эта функция start должна вызываться после того, как объект полностью сконструирован, и поэтому не может быть вызвана изнутри конструктора, как вы делаете.