Как исправить чисто виртуальную функцию, называемую ошибкой во время выполнения?

Я понимаю, почему я получаю ошибку, которую я получаю (вызывается чисто виртуальная функция). Я пытаюсь вызвать чисто виртуальные функции из деструктора моего базового класса, показанного ниже. Однако я не знаю, как переработать мой код, чтобы этого не произошло. Вот базовые и производные классы (в любом случае соответствующие части):

Базовый класс:

TailFileManager::TailFileManager(const std::string &filename, const int fileOpenPeriod_ms)
: m_Stop(false)
{
m_WorkerThread.reset(new boost::thread(boost::bind(&TailFileManager::TailFile, this, filename, fileOpenPeriod_ms)));
}

TailFileManager::~TailFileManager()
{
m_Stop = true;
m_WorkerThread->join();
}

void TailFileManager::TailFile(const std::string &filename, const int fileOpenPeriod_ms)
{
std::ifstream ifs(filename.c_str());

while (! ifs.is_open())
{
boost::this_thread::sleep(boost::posix_time::milliseconds(fileOpenPeriod_ms));
ifs.open(filename.c_str());
}

ifs.seekg(0, std::ios::end);

while (! m_Stop)
{
ifs.clear();

std::string line;

while (std::getline(ifs, line))
{
OnLineAdded(line);
}

OnEndOfFile();
}

ifs.close();
}

Производный класс:

ETSLogTailFileManager::ETSLogTailFileManager(const std::string &filename, const int heartbeatPeriod_ms)
: TailFileManager(filename, heartbeatPeriod_ms),
m_HeartbeatPeriod_ms(heartbeatPeriod_ms),
m_FoundInboundMessage(false),
m_TimeOfLastActivity(0)
{
}

ETSLogTailFileManager::~ETSLogTailFileManager()
{
}

void ETSLogTailFileManager::OnLineAdded(const std::string &line)
{
// do stuff...
}

void ETSLogTailFileManager::OnEndOfFile()
{
// do stuff...
}

3

Решение

Вы не должны вызывать виртуальные функции во время строительства или уничтожения, потому что вызовы не будут делать то, что вы думаете, и если бы они сделали, вы все равно были бы несчастны. Если вы выздоравливающий программист на Java или C #, обратите пристальное внимание на этот пункт, потому что это место, где эти языки изменяются, в то время как C ++ заглатывает.

Переработайте свой дизайн, то есть вы можете вызвать некоторую функцию очистки до того, как объект будет уничтожен, идея состоит в том, чтобы просто избегать виртуальных функций во время const / dest (если они есть!), Если вы работаете с C ++ …

Правила для виртуального вызова разные. C ++ 2003, раздел 12.7 «Строительство и разрушение», говорит:

Давайте освежим некоторые старые воспоминания …

Функции-члены, включая виртуальные функции (10.3), могут вызываться во время создания или уничтожения (12.6.2). Когда виртуальная функция вызывается прямо или косвенно из конструктора (в том числе из mem-initializer для члена данных) или из деструктора, и объект, к которому применяется вызов, является объектом, находящимся в процессе создания или уничтожения, вызываемая функция является один определен в собственном классе конструктора или деструктора или в одной из его баз, но не является функцией, переопределяющей его в классе, производном от класса конструктора или деструктора, или переопределяющим его в одном из других базовых классов самого производного объекта (1.8 ). Если вызов виртуальной функции использует явный доступ к члену класса (5.2.5), а выражение объекта ссылается на объект, находящийся в процессе строительства или уничтожения, но его тип не является ни собственным классом конструктора, ни деструктором, ни одной из его баз, результат звонок не определен.

Из-за этого различия в поведении рекомендуется никогда не вызывать виртуальную функцию объекта, пока он создается или уничтожается.

Никогда не вызывайте виртуальные функции во время строительства или разрушения
Выдержка из Effective C ++, третье издание
Скотт Мейерс

6 июня 2005 г.

http://www.artima.com/cppsource/nevercall.html

10

Другие решения

Что касается стандарта C ++:

  • если вы вызываете виртуальную функцию в конструкторе или деструкторе, то функция
    динамически отправляется, как если бы его динамический тип был типом текущего выполняемого конструктора / деструктора (§12.7 / 4)
  • если эта функция произошла с чисто виртуальным, то это неопределенное поведение (§10.4 / 6); Itanium ABI определяет поведение: __cxa_pure_virtual называется.

Итак, у вас есть небольшая проблема …


Возможное решение проблемы — разбить ее на две части, чтобы разрушить разрушение на две части. Это может быть достигнуто с Strategy шаблон:

  • обеспечить настраиваемый интерфейс, ваша стратегия
  • обеспечить класс менеджера, который инкапсулирует функциональность и зависит от стратегии для настраиваемых частей

Давайте сделаем это более понятным:

class Interface {
public:
friend class Manager;

private:
virtual void finalize() = 0;
}; // class Interfaceclass Manager {
public:
explicit Manager(std::unique_ptr<Interface>&&);

~Manager();

private:
std::unique_ptr<Interface> _interface;
}; // class Manager

Manager::~Manager() {
_interface->finalize();
}

Трюк ? В точке, где finalize() называется уничтожением _interface еще не началось! Призыв к деструктору произойдет позже; и, таким образом, вы не страдаете от судьбы полумертвого объекта.

Я закончу этот ответ предупреждением о join-связать нить в деструкторе сейчас. Помните, что деструкторы автоматически вызываются в случае разматывание стека, поэтому может быть опасно ждать бесконечно провал; особенно если поток ожидает данных, которые должны быть предоставлены в настоящее время разматываясь один … классический случай тупика.


Рекомендации (N3337):

§12.7 / 4 Функции-члены, включая виртуальные функции (10.3), могут вызываться во время создания или уничтожения (12.6.2). Когда виртуальная функция вызывается прямо или косвенно из конструктора или деструктора, в том числе во время создания или уничтожения нестатических членов-данных класса, и объект, к которому применяется вызов, является объектом (называемым x), находящимся в стадии разработки или уничтожение, вызываемая функция является окончательным переопределением в классе конструктора или деструктора, а не переопределением его в более производном классе.

§10.4 / 6 Функции-члены могут вызываться из конструктора (или деструктора) абстрактного класса; эффект от виртуального вызова (10.3) чисто виртуальной функции прямо или косвенно для объекта, создаваемого (или уничтожаемого) из такого конструктора (или деструктора), не определен.

6

Ты пишешь,

«Я пытаюсь вызывать чисто виртуальные функции из деструктора моего базового класса, показанного ниже».

И рассматриваемый код

TailFileManager::~TailFileManager()
{
m_Stop = true;
m_WorkerThread->join();
}

К счастью, в однопоточном исполнении это не могло вызвать чисто виртуальную функцию. Но нить, что ты joinВ этом объекте может вызываться чисто виртуальная функция, возможно, через не виртуальную функцию-член. Если это так, то проблема связана с многопоточностью, а именно с управлением временем жизни этого объекта.

К сожалению, вы не показываете соответствующий код. Попробуйте свести вещи к маленькому, полному, работающему примеру. Где «работает» в том смысле, что воспроизводит проблему.

5
По вопросам рекламы [email protected]