Я понимаю, почему я получаю ошибку, которую я получаю (вызывается чисто виртуальная функция). Я пытаюсь вызвать чисто виртуальные функции из деструктора моего базового класса, показанного ниже. Однако я не знаю, как переработать мой код, чтобы этого не произошло. Вот базовые и производные классы (в любом случае соответствующие части):
Базовый класс:
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...
}
Вы не должны вызывать виртуальные функции во время строительства или уничтожения, потому что вызовы не будут делать то, что вы думаете, и если бы они сделали, вы все равно были бы несчастны. Если вы выздоравливающий программист на 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 г.
Что касается стандарта C ++:
__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) чисто виртуальной функции прямо или косвенно для объекта, создаваемого (или уничтожаемого) из такого конструктора (или деструктора), не определен.
Ты пишешь,
«Я пытаюсь вызывать чисто виртуальные функции из деструктора моего базового класса, показанного ниже».
И рассматриваемый код
TailFileManager::~TailFileManager()
{
m_Stop = true;
m_WorkerThread->join();
}
К счастью, в однопоточном исполнении это не могло вызвать чисто виртуальную функцию. Но нить, что ты join
В этом объекте может вызываться чисто виртуальная функция, возможно, через не виртуальную функцию-член. Если это так, то проблема связана с многопоточностью, а именно с управлением временем жизни этого объекта.
К сожалению, вы не показываете соответствующий код. Попробуйте свести вещи к маленькому, полному, работающему примеру. Где «работает» в том смысле, что воспроизводит проблему.