2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




 
 в чем все-таки ошибка?
Сообщение29.08.2012, 22:21 
Аватара пользователя
Здравствуйте!
Дали такую задачку: дана несложная программа на С++, которая демонстрирует асинхронный вызов функции. Необходимо в ней найти ошибку.
Код:
#include <windows.h>
#include <iostream>
#include <sstream>
#include <process.h>

namespace TST
{
    class Exception
    {
    public:
        Exception(const wchar_t* szwText)
            :   m_wstrText(szwText)
        {;};

        std::wstring What() const throw()
        {
            return m_wstrText;
        };

    protected:
        const std::wstring m_wstrText;
    };

    void le_chk(BOOL bResult, const char* file, int line)
    {
        if(!bResult)
        {
         std::clog << "An error " << GetLastError() << "occured";
            std::wostringstream os;
            os  << L"Win32 error occured: GetLastError returned " << GetLastError()
                << " in " << file << L"@" << line;
            throw Exception(os.str().c_str());
        };
    };

    #define TST_LE_CHECK(res)   le_chk((res), __FILE__, __LINE__)

    class CallBase
    {
    public:
        virtual void Call() = 0;
    };

    class AsyncActionBase
    {
    public:
        AsyncActionBase(CallBase* pCall)
            :   m_hThread(NULL)
        {
            unsigned uId = 0;
            m_hThread = (HANDLE)_beginthreadex(
                            NULL, 0, &AsyncActionBase::thread_proc,
                            (void*)pCall, 0, &uId);
            TST_LE_CHECK(!!m_hThread); // зачем двойное отрицание?
        };

        virtual ~AsyncActionBase()
        {
            if(m_hThread)
            {
                TST_LE_CHECK(WaitForSingleObject(m_hThread, INFINITE)
                    != WAIT_FAILED);
                TST_LE_CHECK(CloseHandle(m_hThread));
                m_hThread = NULL;
            };
        };
    protected:
        static unsigned __stdcall thread_proc(void * p)
        {
            try
            {
                ((CallBase*)p)->Call();
            }
            catch (const TST::Exception& excpt)
            {
                std::wcerr << excpt.What();
            };
            return 0;
        };
        HANDLE m_hThread;
    };

    class MyAsyncAction1
        :   public CallBase
        ,   public AsyncActionBase
    {
    public:
        MyAsyncAction1()
            :   AsyncActionBase(this) // передавать this в списке инициализации конструктору базового класса небезопасно,
        {                        // т.к. конструктор базового класса может воспользоваться еще неинициализированными
                                 //  членами производного класса
        };
       
        virtual ~MyAsyncAction1()
        {
        };

    //CallBase
        virtual void Call()
        {
            for(size_t i = 0; i < 100; ++i)
            {
                std::cout << "Test #" << (i+1) << std::endl;
                Sleep(10);
            };
        };
    };
};

int main()
{
    try
    {
      TST::MyAsyncAction1 oBj;
    }
    catch (const TST::Exception& excpt)
    {
        std::wcerr << excpt.What();
    };
};

Синтаксических ошибок нет. Прочесал весь код, но и логических ошибок не нашел. Запускается и завершается нить корректно, сигнатура функции Call верная, механизм обработки исключений тоже правильный..
Вообщем, я пришел к выводу, что ошибок здесь нет, разве что одна потенциальная ошибка и двойное отрицание (я отметил комментариями)
Ваше мнение?
Заранее спасибо!

 
 
 
 Re: в чем все-таки ошибка?
Сообщение29.08.2012, 22:43 
ИМХО, второй комментарий и есть ошибка. CallBase подобъект может быть ещё не инициализирован к тому моменту, где вызывается виртуальный метод Call(). Вам может повезти, и основной поток успеет завершить инициализацию до момента использования, но может и не повезти.

 
 
 
 Re: в чем все-таки ошибка?
Сообщение29.08.2012, 22:52 
venco в сообщении #612405 писал(а):
CallBase подобъект может быть ещё не инициализирован к тому моменту, где вызывается виртуальный метод Call().
Так конструкторы базовых классов вызываются (вроде бы) в порядке упоминания этих классов в списке наследования, так что все в порядке.

 
 
 
 Re: в чем все-таки ошибка?
Сообщение29.08.2012, 22:59 
Да, но конструктор MyAsyncAction1, который, по идее, и должен в начале прописать новую таблицу виртуальных функций с MyAsyncAction1::Call() будет выполнен после конструктора AsyncActionBase. Т.е. вместо AsyncActionBase::Call() может вызваться CallBase::Call(), которого нет. Как следствие - attempt to call pure virtual function.

 
 
 
 Re: в чем все-таки ошибка?
Сообщение29.08.2012, 23:18 
venco
venco в сообщении #612408 писал(а):
конструктор MyAsyncAction1, который, по идее, и должен в начале прописать новую таблицу виртуальных функций с MyAsyncAction1::Call() будет выполнен после конструктора AsyncActionBase.
Точно! А чтобы убедиться в этом, можно вставить Sleep в конце конструктора AsyncActionBase.

 
 
 
 Re: в чем все-таки ошибка?
Сообщение30.08.2012, 00:39 
Аватара пользователя
Прям чувствовал, что весь подвох в конструкторе MyAsyncAction1... Мне казалось, что связывание происходит на этапе компиляции! Поэтому я посчитал, что к моменту вызова конструктора AsyncActionBase, таблица виртуальных методов MyAsyncAction1 будет уже связана.
EtCetera
Да, Sleep'ом удалось получить pure virtual function call =).
Спасибо, ребята!

 
 
 [ Сообщений: 6 ] 


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group