Неожиданное поведение виртуальной функции? - PullRequest
3 голосов
/ 12 июля 2011

Когда я запускаю следующий код C ++ с Visual Studio 2010, программа застревает при удалении переменной, если какая-либо из функций производного класса объявлена ​​как виртуальная. Может кто-нибудь объяснить это?

void testInheritance()
{
    class a
    {
        public :
            char x;
            void fn1()
            {
                std::cout<<"\n In Class A Function 1 : "<<x;
            }
            virtual void fn2()
            {
                std::cout<<"\n In Class A Function 2 : "<<x;
            }
            a()
            {
                x='A';
                std::cout<<"\n In A() : "<<x;
            }
            ~a()
            {
                std::cout<<"\n In ~A : "<<x;
            }
    };

    class b: public a
    {
        public :
            char y;
            virtual void fn1()
            {
                std::cout<<"\n In Class B Function 1 : "<<y;
            }
             void fn3()
            {
                std::cout<<"\n In Class B Function 3 : "<<y;
            }
            b()
            {
                y='B';
                std::cout<<"\n In B() : "<<y;
            }
            ~b()
            {
                std::cout<<"\n In ~B : "<<y;
            }
    };

    a* var = new b();
    delete var;
}

Подробнее:

Я понимаю, что для вызова b :: fn1 и деструктора класса b мне нужно объявить их виртуальными в базовом классе, то есть в классе a. Но если я этого не сделаю и даже не объявлю какую-либо функцию из класса b (и ни из класса a) как виртуальную, она должна вызывать как fn1, так и деструкторы a, и это происходит идеально. Но когда я объявляю любой элемент b (но не a) виртуальным, будь то новый или перегруженный член, он зависает при компиляции с VS2010 и прерывается при компиляции с gcc4.4.4 в linux. Он должен был вызывать любой из деструкторов и работать нормально, но я не мог понять причину, по которой программа прерывается.

Далее, при использовании Intellitrace в Visual Studio 2010, я пытаюсь разбить код в том месте, где он зависает, я получаю следующее сообщение:

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

Ответы [ 5 ]

4 голосов
/ 12 июля 2011

Вы ожидаете Неожиданное поведение , потому что вы создали Неопределенное поведение в вашей программе.

Удаление объекта класса derived с использованием указателя на класс base, который имеет non-virtual destructor, приводит к Undefined Behavior. Неопределенное поведение означает, что все может случиться.

C ++ Стандартный раздел 1.3.24 гласит:

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

Как решить проблему?

Деструктор в Базовом классе должен быть сделан виртуальным.

2 голосов
/ 12 июля 2011

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

2 голосов
/ 12 июля 2011

вам нужно объявить деструктор виртуальный

1 голос
/ 12 июля 2011

Если «застрял» означает, что b::~b() не вызывается, то ответ таков: a::~a() должно быть virtual.

Вы используете указатель базового класса (a) надержите объект class b.Когда вы delete var;, он вызывает только a::~a(), который не virtual;сделав это virtual;деструкторы вызываются для a и b в правильном порядке.

[Примечание: другим способом его можно ударить, только если вы поставили точку останова где-то и не проходили.:)]

0 голосов
/ 12 июля 2011

Мне на самом деле надоело видеть в тестах C ++ вопрос о том, как будет вести себя в этой ситуации.Они хотят, чтобы вы ответили, что он вызовет деструктор А, а не Б.

Это не гарантированное поведение, и вы не можете на него полагаться.Неопределенное поведение означает, что вы не можете быть уверены в том, что произойдет, и это имеет место здесь.

Это также пример "просто не делайте этого ..".В моей последней работе я полностью удалил тест из системы, которая проверяла это поведение на том основании, что оно неуместно и не по теме.

Альтернативный вариант создания виртуального деструктора a - сделать его защищенным.,Это также защитит вас в том, что main() не удастся скомпилировать, поскольку вы не можете вызвать delete var оттуда.Вы даже не можете вызвать неопределенное поведение в b, выполнив то же самое, что делает main, потому что вы можете быть удивлены, но delete для a* также будет недоступен.

boost::shared_ptr<a>( new b );

безопасно, так как это создаст удалитель для b, а не для a.

Поскольку в a есть еще одна виртуальная функция, хотя вы почти наверняка должны выбрать виртуальный деструктор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...