Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Вызов виртуального метода потомка из базового 
V
    Опции темы
andrew_121
Дата 25.1.2010, 08:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодофей
****


Профиль
Группа: Завсегдатай
Сообщений: 3448
Регистрация: 3.1.2008

Репутация: 6
Всего: 33



есть такой класс:
Код

struct thread_base {
    thread_base() {
        pthread_create(&thread, NULL, (void*(*)(void*))thread_func, (void*)this);
        pthread_join(thread, NULL);
    }
protected:
    virtual void run() = 0;
private:
    static void* thread_func(void* arg) {
        thread_base* pthread = reinterpret_cast<thread_base*>(arg);
        std::cout << "thread_func() arg = " << arg << std::endl << std::flush; /* это выводится */
        pthread->run();
    }
    pthread_t thread;
};

из него выведен такой:
Код

struct thread_stage1 : public thread_base {
    thread_stage1(double p1, double p2, const double& v)
    :thread_base(),min(p1),max(p2),val(v)
    {
        std::cout << "thread_stage1" << std::endl << std::flush; /* это не выводится */
    }
    virtual void run() {
        std::cout << "данные приняты первой ступенью" << std::endl << std::flush; /* и это не выводится */
    }
    private:
        double min;
        double max;
        const double& val;
};

при запуске вот что выводит:
Цитата

thread_func() arg = 0xbffe5b5c
pure virtual method called
terminate called without an active exception
Aborted

из этого понятно что программа рушится обрабатывая конструктор базового класса.
где ступил?
linux, g++

Это сообщение отредактировал(а) andrew_121 - 25.1.2010, 08:40


--------------------
Удалил аккаунт. Прощайте!
PM MAIL   Вверх
mes
Дата 25.1.2010, 09:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(andrew_121 @  25.1.2010,  07:38 Найти цитируемый пост)
pure virtual method called


Цитата(andrew_121 @  25.1.2010,  07:38 Найти цитируемый пост)
обрабатывая конструктор базового класса.

нельзя в конструкторе прямо или косвенно вызывать чисто-виртуальные методы..
для (всех) виртуальных функций действует правило - вызывается тело виртуальной функции базового класса, так как наследник еще не сформирован..


Это сообщение отредактировал(а) mes - 25.1.2010, 09:50


--------------------
PM MAIL WWW   Вверх
andrew_121
Дата 25.1.2010, 10:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодофей
****


Профиль
Группа: Завсегдатай
Сообщений: 3448
Регистрация: 3.1.2008

Репутация: 6
Всего: 33



mes, так "thread_func()" это статический метод. т.е. в потомке он будет(должен) вызывать переопределенный метод потомка. или не так?
в общем, как исправить?


--------------------
Удалил аккаунт. Прощайте!
PM MAIL   Вверх
Леопольд
Дата 25.1.2010, 10:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

Репутация: 10
Всего: 13



Цитата(andrew_121 @  25.1.2010,  08:38 Найти цитируемый пост)
thread_base* pthread = reinterpret_cast<thread_base*>(arg);

Обязательно через void* передавать?


Так всё работает. Значит проблема в использовании.
Код

#include <iostream>

struct IParent{
protected:
    virtual void call() = 0;
public:
    static void delegateCall(IParent& iface){
        iface.call();
    }

    typedef void (*Delegate)(IParent&);
};
struct Child: public IParent{
    virtual void call(){
        std::cout<<"Child::call"<<std::endl;
    };
};
void caller(IParent::Delegate deleg){
    Child obj;
    deleg(obj);
}

int main(){

    caller(&IParent::delegateCall);
    
    return 0;
}



--------------------
вопросов больше чем ответов
PM MAIL   Вверх
Alca
Дата 25.1.2010, 10:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3993
Регистрация: 14.6.2006

Репутация: 7
Всего: 50



а если так?
Код

struct thread_stage1 : public thread_base {
    thread_stage1(double p1, double p2, const double& v)
    :thread_base(),min(p1),max(p2),val(v)
    {
        std::cout << "thread_stage1" << std::endl << std::flush; /* это не выводится */
    }
protected:  //<<<<<<<<<<<<<!!!!!!!!!!!!!!!!!!!!!!!!
    virtual void run() {
        std::cout << "данные приняты первой ступенью" << std::endl << std::flush; /* и это не выводится */
    }
    private:
        double min;
        double max;
        const double& val;
};


Это сообщение отредактировал(а) Alca - 25.1.2010, 10:31


--------------------
PM WWW ICQ Skype Jabber   Вверх
Lazin
Дата 25.1.2010, 10:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



andrew_121, у тебя run вызывается в конструкторе
PM MAIL Skype GTalk   Вверх
Alca
Дата 25.1.2010, 10:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3993
Регистрация: 14.6.2006

Репутация: 7
Всего: 50



Цитата

andrew_121, у тебя run вызывается в конструкторе

где именно?


--------------------
PM WWW ICQ Skype Jabber   Вверх
mes
Дата 25.1.2010, 10:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(andrew_121 @  25.1.2010,  09:17 Найти цитируемый пост)
так "thread_func()" это статический метод.


Цитата(mes @  25.1.2010,  08:48 Найти цитируемый пост)
прямо или косвенно

так в этой статической вызывается виртуальная smile

Цитата(andrew_121 @  25.1.2010,  09:17 Найти цитируемый пост)
в общем, как исправить? 

можно применить "статическую виртуализацию" (CRTP Patern) если полиморфизм не нужен
либо стартер-враппер  или принимать функциональный объект.
в любом случае перепроектировка класса.


--------------------
PM MAIL WWW   Вверх
andrew_121
Дата 25.1.2010, 10:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодофей
****


Профиль
Группа: Завсегдатай
Сообщений: 3448
Регистрация: 3.1.2008

Репутация: 6
Всего: 33



Цитата(Леопольд @  25.1.2010,  10:29 Найти цитируемый пост)
Обязательно через void* передавать?

да. pthread_create() требует указатель на функцию типа void* (*)(void*)


Цитата(Lazin @  25.1.2010,  10:30 Найти цитируемый пост)
andrew_121, у тебя run вызывается в конструкторе 

точно! потомок же еще не создан. сейчас проверю.


--------------------
Удалил аккаунт. Прощайте!
PM MAIL   Вверх
Леопольд
Дата 25.1.2010, 10:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

Репутация: 10
Всего: 13



Цитата(andrew_121 @ 25.1.2010,  08:38)
есть такой класс:
Код

struct thread_base {
    thread_base() {
        pthread_create(&thread, NULL, (void*(*)(void*))thread_func, (void*)this);
        pthread_join(thread, NULL);
    }

В конструкторе thread_base передаётся this в pthread_create. Тут потенциальная ошибка. Сначала создаются базовые классы, потом наследники. В конструкторе thread_base можно считать созданной только базовую часть объекта а значит в vtbl нет указателей на переопределённые функции потомка. Поэтому, прямо или косвенно, никак нельзя вызвать то чего ещё нет. По всей видимости именно такая попытка и делается.

Но ничего не мешает вызвать pthread_create из конструктора последнего потомка. Не стоит расценивать как предложение реализовать таким образом. Сам я бы так не сделал smile Код приведён для того, чтобы просто подтвердить вышенаписанное...

Код

#include <iostream>

struct IParent{
protected:
    virtual void call() = 0;
public:
    static void delegateCall(IParent& iface){
        iface.call();
    }

    typedef void (*Delegate)(IParent&);
};
void caller(IParent& ref, IParent::Delegate deleg){
    deleg(ref);
}
struct Child: public IParent{
    Child(){
        caller(*this, &(IParent::delegateCall));
    }
    virtual void call(){
        std::cout<<"Child::call"<<std::endl;
    };
};

int main(){

    Child();
    
    return 0;
}


Это сообщение отредактировал(а) Леопольд - 25.1.2010, 11:12


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
andrew_121
Дата 25.1.2010, 11:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодофей
****


Профиль
Группа: Завсегдатай
Сообщений: 3448
Регистрация: 3.1.2008

Репутация: 6
Всего: 33



добавил в базовый класс метод start(), и все заработало.
Код

/* базовый класс потока */
struct thread_base {
    thread_base() {}
    void start() {
        pthread_create(&thread, NULL, (void*(*)(void*))thread_func, (void*)this);
        pthread_join(thread, NULL);
    }
protected:
    virtual void run() = 0;
    static void* thread_func(void* arg) {
        thread_base* pthread = reinterpret_cast<thread_base*>(arg);
        pthread->run();
    }
private:
    pthread_t thread;
};

но любопытно. каким образом можно реализовать запуск потока из конструктора предка, без функциональных объектов?

Цитата(mes @  25.1.2010,  10:37 Найти цитируемый пост)
CRTP Patern

один из выходов.

Добавлено через 58 секунд
Цитата(Леопольд @  25.1.2010,  10:53 Найти цитируемый пост)
В конструкторе thread_base передаётся this в pthread_create. Тут потенциальная ошибка. Сначала создаются базовые классы, потом наследники. В конструкторе thread_base можно считать созданной только базовая часть объекта а значит в vtbl нет указателей на переопределённые функции потомка. Поэтому, прямо или косвенно, никак нельзя вызвать то чего ещё нет. По всей видимости именно такая попытка и делается. 

да, все верно.


--------------------
Удалил аккаунт. Прощайте!
PM MAIL   Вверх
mes
Дата 25.1.2010, 11:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(andrew_121 @  25.1.2010,  10:03 Найти цитируемый пост)
но любопытно. каким образом можно реализовать запуск потока из конструктора предка, без функциональных объектов?

внешним  объектом/функцией который вначале создаст а потом запустит.



--------------------
PM MAIL WWW   Вверх
andrew_121
Дата 25.1.2010, 11:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодофей
****


Профиль
Группа: Завсегдатай
Сообщений: 3448
Регистрация: 3.1.2008

Репутация: 6
Всего: 33



Цитата(mes @  25.1.2010,  11:16 Найти цитируемый пост)
внешним  объектом/функцией который вначале создаст а потом запустит.

понятно. smile
всем спасибо!
вопрос закрыт.


--------------------
Удалил аккаунт. Прощайте!
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0765 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.