Здравствуйте, rus blood, Вы писали:
RB>If you attempt to instantiate a class marked with novtable and then access a class member, you will receive an access violation (AV).
Здравствуйте, Pavel Dvorkin, Вы писали:
К>>Что должен сказать компилятор , и что должна сделать программа, если она скомпилируется ? PD>Сказать, что детям играть со спичками declspec(novtable) не разрешается.
Должен, но не говорит.
По сути, __declspec(novtable) автоматически делает все виртуальные функции чисто виртуальными.
Но, так как это расширение языка, т.е. заплатка поверх парсера, то никакой диагностики мы не получаем — и можем смело создавать экземпляры фактически абстрактного класса.
К>Должен, но не говорит. К>По сути, __declspec(novtable) автоматически делает все виртуальные функции чисто виртуальными. К>Но, так как это расширение языка, т.е. заплатка поверх парсера, то никакой диагностики мы не получаем — и можем смело создавать экземпляры фактически абстрактного класса.
Это ж для MS считай традиционный баг. Помниться, в VC 6 можно было создавать анонимные экземпляры абстрактных классов. С учетом того, что их можно было передавать даже по неконстантной ссылке, было прикольно. Да и потом что-то такое кажется всплывало.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Кодт, Вы писали:
К>>>Что должен сказать компилятор , и что должна сделать программа, если она скомпилируется ? PD>>Сказать, что детям играть со спичками declspec(novtable) не разрешается. К>Должен, но не говорит. К>По сути, __declspec(novtable) автоматически делает все виртуальные функции чисто виртуальными. К>Но, так как это расширение языка, т.е. заплатка поверх парсера, то никакой диагностики мы не получаем — и можем смело создавать экземпляры фактически абстрактного класса.
так оно для этого и предназначено. это костыль для библиотек, создающих, например, COM-объекты. посмотри, как объявляются COM-классы в windows SDK. а вообще, смешной топик: взять дробовик, зарядить его картечью, тщательно прицелиться себе в ногу, нажать на спуск, а потом удивляться, почему это тебе ногу расхерачило.
Здравствуйте, const_volatile, Вы писали:
_>так оно для этого и предназначено. это костыль для библиотек, создающих, например, COM-объекты. посмотри, как объявляются COM-классы в windows SDK. а вообще, смешной топик: взять дробовик, зарядить его картечью, тщательно прицелиться себе в ногу, нажать на спуск, а потом удивляться, почему это тебе ногу расхерачило.
Не совсем так. Этот костыль должен работать в паре с объявлением функций чисто виртуальными.
Его главное назначение — отвадить компилятор создавать ненужные таблицы, а линкер, соответственно, — искать эти таблицы.
(Действительно: кому нужна таблица, в которой все элементы — _purecall ?)
Здравствуйте, Кодт, Вы писали:
К>Линкеру?
линкеру не мешает. мешает, видимо, тому, кто ждет линкер и надеется получить некий профит по времени сборки или размеру бинаря
К>Занимает место в секции констант.
я так понимаю, что для тебя это очень критично (кстати, почему?). мне это не критично (ну пока я не знаю, почему я должен этого бояться) и стандарто-писателям тоже
ты строковые константы не используешь в коде ради экономии места в секции констант? считываешь их из файла или сети, когда надо что-то напечатать на экран или через beep азбукой морзе что-то сообщаешь?
Здравствуйте, uzhas, Вы писали:
U>я так понимаю, что для тебя это очень критично (кстати, почему?).
Для меня это совершенно некритично.
Просто я прочитал статью на хабре, проверил — и схватился за голову: какая шикарная грабля.
Главное, что авторам PVS Studio пишут претензии: мол, почему вы ругаетесь на memcpy не-POD-ов, если там явно сказано __declspec(novtable).
Значит, кто-то ведь этим пользуется. А на самом деле, это пенопластовый бронежилет, с пожизненной гарантией: как только прострелили, так гарантия кончилась.
Здравствуйте, Alexander G, Вы писали:
AG>А может, чтобы не генерировать (и, соответственно, не вызывать) конструктор совсем?
Ну и это тоже.
Правда, такой конструктор (определяемый компилятором) отлично инлайнится.
В дебаге он должен делать одно бестолковое действие — присвоить vptr'у значение, которое тут же будет переприсвоено.
В релизе это вообще выродится.
Теоретические проблемы могут возникнуть, если интерфейс объявлен в нескольких модулях. Поскольку у него нет спецификации dllexport/dllimport, то, формально, мы получим нарушение ODR — несколько разных экземпляров vtable (хотя и идентичных). Ну и пёс с ними, на самом деле.
Ещё одна закавыка, если включено RTTI. Всякая служебная информация о типе находится тоже в vtable под правильными отрицательными смещениями.
Если несколько vtable разных, то может накрениться dynamic_cast. Если таблицы вообще не созданы, программа может непредсказуемо рухнуть.
Ибо нефиг смешивать RTTI и COM.
Здравствуйте, Кодт, Вы писали:
К>По сути, __declspec(novtable) автоматически делает все виртуальные функции чисто виртуальными.
Он убирает генерацию vtable. Полезно для базовых классов. Это не то же, что чисто-виртуальные ф-ии. В наследнике эти ф-ии унаследуются со всем своим не чисто-виртуальным телом.
Здравствуйте, Кодт, Вы писали:
К>Не совсем так. Этот костыль должен работать в паре с объявлением функций чисто виртуальными.
Необязательно. При единичном наследовании в конструкторе наследника все-равно затирается vptr базы, т.е. таблица базы просто не нужна. А не чисто виртуальные ф-ии от базы — нужны.
Здравствуйте, Кодт, Вы писали:
К>Теоретические проблемы могут возникнуть, если интерфейс объявлен в нескольких модулях. Поскольку у него нет спецификации dllexport/dllimport, то, формально, мы получим нарушение ODR — несколько разных экземпляров vtable (хотя и идентичных).
Не должно. В бинарнике должен быть vtable только если один из модулей бинарника ссылается на конструктор типа с vtable.
К>Ещё одна закавыка, если включено RTTI. Всякая служебная информация о типе находится тоже в vtable под правильными отрицательными смещениями.
Если экземпляры базового типа не создаются, то никаких проблем.
К>Если несколько vtable разных, то может накрениться dynamic_cast.
Он и накреняется именно в виндах. Чудес не бывает. ))
Экземпляры, созданные модулями из разных DLL, обычно "не видят" друг друга. Да и как, собсно? В Linux эта проблема решается уровнем видимости символов — по умолчанию линкер не грузит повторяющиеся символы из других модулей, поэтому на всех получается одна vtable и общий RTTI. В виндах — увы, увы.
К>Если таблицы вообще не созданы, программа может непредсказуемо рухнуть.
Каким образом? Созданные таблицы связываются в цепочки. Если ты получил экземпляр объекта-наследника, созданного в другом модуле, т.е. у него есть живая ссылка на его vtable, ты будешь бегать по вполне живым таблицам rtti из того модуля. Не найдешь, правда, ничего... но ничего не рухнет.
К>Ибо нефиг смешивать RTTI и COM.
Легко ))
Толку, правда, мало.
======
В плагинных RTTI-based фреймворках обычно есть некая общая DLL, из которой экспортируются нужные интерфейсы. Хост и плагины ссылаются на эту DLL, поэтому RTTI вокруг определённых в ней интерфейсов вполне пашет.