Russian Qt Forum
Ноябрь 15, 2024, 10:26 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Использование чистой виртуальной функции в конструкторе  (Прочитано 13281 раз)
lcs-perm
Гость
« : Май 18, 2013, 07:20 »

Добрый день.
Пытаюсь разобраться со следующим кодом:
Код:
#include <iostream>

using namespace std;

class AbstractClass
{
protected:
  virtual void AbstractFunc() = 0;
public:
  AbstractClass();
};

AbstractClass::AbstractClass()
{
  AbstractFunc();
}

int main()
{
  return 0;
}
На этот код при компиляции мне выдается сразу куча сообщений:
Код:
In constructor 'AbstractClass::AbstractClass()':
warning: abstract virtual 'virtual void AbstractClass::AbstractFunc()' called from constructor
undefined reference to `AbstractClass::AbstractFunc()'
undefined reference to `AbstractClass::AbstractFunc()'
collect2: ld returned 1 exit status
Причем, если сделать AbstractFunc() просто виртуальной - все становится просто замечательно.
Помогите разобраться, пожалуйста
Записан
Странник
Гость
« Ответ #1 : Май 18, 2013, 08:07 »

ну а что вы ожидали получить, вызывая чисто виртуальную функцию virtual void AbstractClass::AbstractFunc() = 0, реализация для которой отсутствует? вы должны реализовать перегруженную функцию в наследнике AbstractClass и вызывать ее в конструкторе наследника для получения желаемого эффекта. также вы сможете обращаться к ней посредством указателя типа базового класса на объект наследника.
« Последнее редактирование: Май 18, 2013, 08:09 от Странник » Записан
lcs-perm
Гость
« Ответ #2 : Май 18, 2013, 08:30 »

ну а что вы ожидали получить, вызывая чисто виртуальную функцию virtual void AbstractClass::AbstractFunc() = 0, реализация для которой отсутствует?
Немного модифицировал код, чтоб компилировалось без ошибок
Код:
class AbstractClass
{
protected:
  virtual void AbstractFunc() { cout << "AbstractClass" << endl; }
public:
  AbstractClass();
};

class RealClass: public AbstractClass
{
protected:
  void AbstractFunc() { cout << "RealClass\n" << endl; }
};

AbstractClass::AbstractClass()
{
  AbstractFunc();
}

int main()
{
  RealClass rc;
  return 0;
}
На выходе получаю "AbstractClass". А хотелось бы получить "RealClass"
Записан
Majestio
Гость
« Ответ #3 : Май 18, 2013, 08:33 »

У наследника функция уже не виртуальная?
Записан
lcs-perm
Гость
« Ответ #4 : Май 18, 2013, 08:36 »

У наследника функция уже не виртуальная?
Почему не виртуальная. Виртуальность же наследуется.
В любом случае - добавление virtual в наследника ничего не дает
Записан
Bepec
Гость
« Ответ #5 : Май 18, 2013, 08:42 »

Сталкивался с такой хотелкой Улыбающийся

Не пытайтесь вызвать функцию из конструктора - ничего не получится.

НО.

Если вызвать её QMetaObject::invokeMethod(this, "AbstractFunc"); то тогда всё пройдет так, как вы хотите. Функция в таком случае должна быть слотом.

Вкратце - в конструкторе у вас её нет. Но если оставить в очереди событий вызов этой функции, он выполнится сразу после конструктора и функция будет уже не предка, а наследника.

PS в С++ такое реализовать будет очень трудно, а в Qt с помощью сигнал-слотов более менее легко.
« Последнее редактирование: Май 18, 2013, 08:45 от Bepec » Записан
Majestio
Гость
« Ответ #6 : Май 18, 2013, 08:43 »

Я знак вопроса лишний поставил. При вызове виртуальной функции из конструктора вызывается та версия, которая определена в данном классе или базового класса, если она в данном классе не перегружена. Вроде так. Для конструктора - формально она не виртуальная.
Записан
lcs-perm
Гость
« Ответ #7 : Май 18, 2013, 09:15 »

Bepec. Ну у меня вопрос скорее даже не как реализовать, а скорее из разряда "хочется понять" (хотя и реализовать, конечно, хочется).
Majestio. Попробую поизучать таблицы виртуальных функций C++. Может и пойму, отчего так
Записан
mutineer
Гость
« Ответ #8 : Май 18, 2013, 09:39 »

Так происходит потому, что во время выполнения конструктора таблица виртуальных функций инициализирована только для текущего класса и его базовых классов. Никакой информации про таблицу виртуальных функций наследников еще нет, поэтому будет вызываться функция текущего конструируемого класса
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Май 18, 2013, 10:28 »

Почему не виртуальная. Виртуальность же наследуется.
В любом случае - добавление virtual в наследника ничего не дает
Дает, но только не в конструкторе/деструкторе базового класса - в этот момент порожденный объект как бы еще не создан (или уже разрушен). Кстати sizeof() также вернет размер базового (а не порожденного) в этот момент.

Это правильно - базовый конструктор выполняется первым и ничего не знает о порожденном.
Записан
mutineer
Гость
« Ответ #10 : Май 18, 2013, 10:30 »

Igors А что дает добавление virtual в наследнике?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Май 18, 2013, 11:00 »

Igors А что дает добавление virtual в наследнике?
Имеется ввиду "добавление виртуальной ф-ции в наследнике", а не написание для нее слова "virtual", его можно писать или нет - дело вкуса, все равно будет виртуальной.

Откуда такое горячее желание "поймать" на незнании азов? Улыбающийся Хотите поизучать язык - давайте вместе. Напр
Так происходит потому, что во время выполнения конструктора таблица виртуальных функций инициализирована только для текущего класса и его базовых классов. Никакой информации про таблицу виртуальных функций наследников еще нет, поэтому будет вызываться функция текущего конструируемого класса
А как это протестировать/распечатать?
Записан
mutineer
Гость
« Ответ #12 : Май 18, 2013, 11:43 »

Откуда такое горячее желание "поймать" на незнании азов? Улыбающийся Хотите поизучать язык - давайте вместе. Напр

Не было у меня никакого желания "поймать". Спросил на случай вдруг оно что-то экзотическое дает, просто я об этом не знаю
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #13 : Май 18, 2013, 12:17 »

А как это протестировать/распечатать?
Для gcc можно так:
Код
C++ (Qt)
#include <iostream>
 
class Base
{
public:
       Base()
       {
               void *vtbl = reinterpret_cast<void**>( this )[ 0 ];
               std::cout << "Constructor Base vtbl = " << vtbl << std::endl;
 
               method();
       };
 
       virtual void    method()
       {
               void *vtbl = reinterpret_cast<void**>( this )[ 0 ];
               std::cout << "Base::method vtbl = " << vtbl << std::endl;
       }
};
 
class Derived : public Base
{
public:
       Derived()
       {
               void *vtbl = reinterpret_cast<void**>( this )[ 0 ];
               std::cout << "Constructor Derived vtbl = " << vtbl << std::endl;
       };
 
       virtual void    method()
       {
               void *vtbl = reinterpret_cast<void**>( this )[ 0 ];
               std::cout << "Derived::method vtbl = " << vtbl << std::endl;
       }
};
 
int main()
{
       std::cout << "----- Build base -----" << std::endl;
       Base base;
       std::cout << "----- Build derived -----" << std::endl;
       Derived derived;
}
 
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Май 18, 2013, 14:14 »

Для gcc можно так:
У меня аналогично (несущественная разница в деталях). Правда никакой он не void (не адрес) а, полагаю, индекс/ID (хорошо видно напр в 64). Хотя это не документировано - никогда не видел др реализации ни в одном компиляторе
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.148 секунд. Запросов: 23.