Чистый виртуальный метод называют

Править: РЕШЕННЫЙ

Я работаю над многопоточным проектом прямо сейчас, где у меня есть основной класс рабочего с переменными классами рабочего, которые наследовались ему. Во времени выполнения классы рабочего становятся потоками, которые тогда выполняют работу по мере необходимости.

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

Я сделал это путем создания указателя на указатель базового класса:

baseWorkerClass** workerPtrArray;

Тогда в конструкторе директора, я динамично выделяю массив указателей на основной класс рабочего:

workerPtrArray = new baseWorkerClass*[numWorkers];

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

Вот то, как директор хранит указатели:

Director::manageWorker(baseWorkerClass* worker)
{
    workerPtrArray[worker->getThreadID()] = worker;
}

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

class workerVariant : protected baseWorkerClass
{
    public:

    workerVariant(int id)
    : id(id)
    {
        Director::manageWorker(this);
    }

    ~workerVariant()
    {
    }

    int getThreadID()
    {
        return id;
    }

    int getSomeVariable()
    {
        return someVariable;
    }

    protected:

    int id;
    int someVariable
};

Тогда baseWorkerClass выглядит примерно так:

class baseWorkerClass
{
public:

    baseWorkerClass()
    {
    }

    ~baseWorkerClass()
    {
    }

    virtual int getThreadID() = 0;
    virtual int getSomeVariable() = 0;
};

После того, как каждый вариант рабочего сделан, инициализировав, я должен закончить с массивом указателей на объекты baseWorkerClass. Это означает, что я должен быть в состоянии, например, получить значение данной переменной в определенном рабочем, использующем ее идентификатор в качестве индекса к массиву, как так:

workerPtrArray[5]->getSomeVariable(); // Get someVariable from worker thread 5

Проблема состоит в том, что этот код вызывает катастрофический отказ в исполняемом файле Windows без любого объяснения того, почему, и в Linux, это говорит:

чистый виртуальный метод называют
оконечный названный без активного исключения
Прерванный

Я, возможно, поклялся, что у меня была эта работа в какой-то момент, таким образом, я смущен относительно того, что я завинтил.


Фактический неизмененный код, который имеет проблему:

Заголовок варианта рабочего: http://pastebin.com/f4bb055c8
Исходный файл варианта рабочего: http://pastebin.com/f25c9e9e3

Основной заголовок класса рабочего: http://pastebin.com/f2effac5
Основной исходный файл класса рабочего: http://pastebin.com/f3506095b

Заголовок директора: http://pastebin.com/f6ab1767a
Исходный файл директора: http://pastebin.com/f5f460aae


Править: Дополнительная информация, в функции manageWorker, я могу вызвать любую из чистых виртуальных функций от указателя "рабочий", и это работает просто великолепно. За пределами функции manageWorker, когда я пытаюсь использовать массив указателей, он перестал работать.

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

10
задан jakogut 30 January 2010 в 22:07
поделиться

6 ответов

Проблема, по-видимому, является тем, что директор :: Rublicworker вызывается в конструкторе экземпляров Workervaryt :

Director::manageWorker(baseWorkerClass* worker) {
    workerPtrArray[worker->getThreadID()] = worker;
}

Предположительно GetThreadid () не Чистая виртуальная функция или вы бы (надеетесь!) получил ошибку компилятора о том, что он не переопределял его в Warevervariate . Но GetThreadid () может вызвать другие функции, которые вы должны переопределить, но вызываются в абстрактном классе. Вы должны дважды проверить определение getThreadid () , чтобы убедиться, что вы не делаете ничего не связанного, что будет зависеть от дочернего класса, прежде чем он будет правильно инициализирован.

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

12
ответ дан 3 December 2019 в 19:32
поделиться

Не видя полного кода, я бы рискнул предположить, что вы выходите за границу блока памяти, на который указывает workerPtrArray. Это, безусловно, имело бы смысл, так как он жалуется на то, что вызывается чистая виртуальная функция. Если разыскиваемая память - это мусор, то во время выполнения не может быть никакого смысла и происходят странные вещи.

Попробуйте разместить утверждения в критических местах, где происходит разыменование массива, чтобы убедиться, что индексы имеют смысл. Т.е. ограничьтесь 4 рабочими местами и убедитесь, что id меньше 4.

3
ответ дан 3 December 2019 в 19:32
поделиться

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

2
ответ дан 3 December 2019 в 19:32
поделиться

Я не видел, как класс варианта построена в любом из ваших образцов кода. Вы уверены, что переданный идентификатор находится в пределах досягаемости для рабочего массива? Кроме того, вы строите объекты, используя «новую», верно? Если вы построили объект в стеке, он зарегистрировался бы с директором, но после того, как конструктор возвращает объект, будет немедленно уничтожен, но режиссер сохранит его указатель на объект, который был на стеке.

Кроме того, ваш деструктор BaseWorkerClass должен быть виртуальным наряду с деструктором Warservariant, чтобы убедиться, что они вызывают при удалении массива BaseWorkerClass.

Из моего комментария к другому вопросу рассмотрим использование STD :: Vector вместо двойного указателя. Легко поддерживать и понимать и удаляет вашу необходимость поддерживать массив.

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

class baseWorkerClass
{
public:

    baseWorkerClass(int id) :
        id( id )
    {
    }

    virtual ~baseWorkerClass()
    {
    }

    int getThreadID(){ return id; };
    virtual int getSomeVariable() = 0;

protected:
    int id;
};

class workerVariant : protected baseWorkerClass
{
    public:

    workerVariant(int id) :
        baseWorkerClass( id )
    {
        Director::manageWorker(this);
    }

    virtual ~workerVariant()
    {
    }

    int getSomeVariable()
    {
        return someVariable;
    }

protected:
    int someVariable
};
1
ответ дан 3 December 2019 в 19:32
поделиться

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

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

Это означает, что при построении чистые виртуальные функции на самом деле являются чисто виртуальными. Современные компиляторы c++ все лучше это улавливают, но во многих случаях можно "похоронить" незаконный вызов так, чтобы компилятор не заметил ошибку.

Мораль истории: не делайте в конструкторе ничего, что вызовет виртуальную функцию. Он просто не будет делать то, что вы ожидаете. Даже когда это не чисто.

2
ответ дан 3 December 2019 в 19:32
поделиться

Разве вы не случайно доступа к объектам после того, как они разрушаются? Поскольку во время разрушения указатели VTable постепенно будут «откатываться», чтобы записи VTable указывают на методы базового класса, некоторые из которых абстрактны. После удаления объекта память может быть оставлена ​​так, как она была во время деструктора базового класса.

Я предлагаю вам попробовать инструменты отладки памяти, такие как VALGRIND или malloc_check_ = 2 . Также на Unix довольно легко получить укладку для таких фатальных ошибок. Просто запустите свое приложение под GDB, или TotalView , и в точке произойдет ошибка, она автоматически остановится, и вы можете посмотреть на стек.

0
ответ дан 3 December 2019 в 19:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: