Компьютерный форум
Правила
Вернуться   Компьютерный форум > Форум программистов > Программирование под Windows > Visual C++
Перезагрузить страницу Declspec novtable хорошее умолчание для абстрактных классов
Ответ
 
Опции темы Опции просмотра
  (#1 (permalink)) Старый
Shunix Shunix вне форума
Member
 
Сообщений: 1,355
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
Регистрация: 07.06.2002
По умолчанию Declspec novtable хорошее умолчание для абстрактных классов - 21.06.2008, 15:44

Всем привет,

С подачи коллеги по работе задумался над вопросом...
VC++ поддерживает конструкцию __declspec(novtable). Познакомился с ней по описанию в MSDN как раз в процессе обсуждения данного вопроса - никогда раньше не использовал и даже не задумывался над необходимостью :).
Вопрос (и на мой взгляд действительно интересный) прозвучал следующий - раз данная среда поддерживает подобные "трюки", почему подобное поведение не определяется как default для любого абстрактного класса (т.е. имеющего в своем составе как минимум один чисто виртуальный метод).
В чем подвох? Учитывая, что написано в MSDN - "Using this form of __declspec can result in a significant reduction in code size" - получается, что каждый должен быть только рад подобному "умолчанию". По поводу "significant reduction in size" тоже одолевают сомнения - вроде бы ссылка на таблицу на класс плюс сама таблица - так ли это значительно сокращает размер выходного файла. Не уверен...

Будет интересно узнать, у кого какие мысли имеются на данный счет?

С уважением,
Shunix.
Ответить с цитированием
  (#2 (permalink)) Старый
Влад Влад вне форума
Специалист
 
Сообщений: 3,884
Сказал(а) спасибо: 1
Поблагодарили 25 раз(а) в 25 сообщениях
Регистрация: 27.06.2002
Адрес: Санкт-Петербург
По умолчанию 24.06.2008, 15:01

Спасибо коллеге Shunix'у за интересный вопрос!
Почему подобное поведение не определяется как default для любого абстрактного класса (т.е. имеющего в своем составе как минимум один чисто виртуальный метод? Вот код (довольно таки надуманный, но иллюстрирующий возникающую проблему):
Код:
#include "stdafx.h"
#include <iostream>
using namespace std;

class Device
{
public:
    Device()
    {
        cout << "Device ctor begins" << endl; 
        Reset(this);
        cout << "Device ctor ends" << endl; 
    } 
    virtual ~Device() { cout << "Device dtor" << endl; }
    virtual void Reset(Device* obj) { cout << "Device::Reset" << endl; obj->Initialization(); }
    virtual void Initialization() {};
};

class 
__declspec(novtable)
BaseComputer: public Device
{
    bool _inited;

public:
    BaseComputer(): _inited(false)
    {
        cout << "BaseComputer ctor begins" << endl; 
        Reset(this);
        cout << "BaseComputer ctor ends" << endl; 
    } 
    virtual ~BaseComputer() { cout << "BaseComputer dtor" << endl; }
    virtual void Reset(Device* obj) { cout << "BaseComputer::Reset" << endl; obj->Initialization(); }
    virtual bool Inited() { return _inited; }
    virtual void Initialization() { _inited = true; }

    virtual void StartDoJob() = 0;
};

class Display: public Device
{
public:
    Display() 
    { 
        cout << "Display ctor begins" << endl; 
        Reset(this);
        cout << "Display ctor ends" << endl;
    } 
    virtual ~Display() { cout << "Display dtor" << endl; }
    virtual void Reset(Device* obj) { cout << "Display::Reset" << endl; obj->Initialization(); }
    virtual void Initialization() {};
};

class Touchpad: virtual public BaseComputer, virtual public Display
{
public:
    Touchpad() { cout << "Touchpad ctor" << endl; } 
    virtual ~Touchpad() { cout << "Touchpad dtor" << endl; }
    virtual void Reset(Device* obj) { cout << "Touchpad::Reset" << endl; obj->Initialization(); }
    virtual void StartDoJob() 
    { 
        if (Inited())
            cout << "Do some useful action: call to Touchpad::StartDoJob" << endl;
        else
            cout << "Oooops! Cant't do anything useful, 'cause object not properly initialized..." << endl;
    }
    virtual void Initialization() {};
};

int _tmain(int argc, _TCHAR* argv[])
{
    Touchpad f;
    f.StartDoJob();
    return 0;
}
Можно поэкспериментировать с этим кодом, комментируя и раскомментируя строку __declspec(novtable) в BaseComputer, который, безусловно, является абстрактным классом, и убедиться, что поведение программы существенно меняется. Нетрудно видеть, что может произойти, если __declspec(novtable) будет определено как default для любого абстрактного класса. Разумеется, подобные эффекты могут проявляться и при других, гораздо более хитрых и неочевидных схемах инициализации объектов, особенно, если соответствующие классы инкапсулированы в коммерческой(-их) библиотеке(-ах) с закрытым исходным кодом; здесь код намеренно упрощен, чтобы проиллюстрировать возникающую проблему.

Поэтому, по-видимому, разработчики компилятора придерживаются такой точки зрения: Как и любое другое страшное оружие, __declspec(novtable) лучше оставить на усмотрение программиста. Вероятно, он все-таки понимает, что делает, и к чему его действия могут привести

Короче, это веревка достаточной длины, чтобы обмотаться ей вокруг тела в три слоя, и все-таки суметь выстрелить себе в ногу


The difference between theory and practice is that in theory, there is no difference between theory and practice, but in practice, there is.
Ответить с цитированием
  (#3 (permalink)) Старый
Romendakil Romendakil вне форума
Member
 
Сообщений: 123
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
Регистрация: 06.05.2004
По умолчанию 25.06.2008, 17:19

Кстати, в MSDN по поводу __declspec(novtable) написано, что его следует использовать не с абстрактными классами вообще, а с "pure interface classes". Мне кажется, это означает вообще присутствие в классе только чисто виртуальных методов, и никаких больше.
Ответить с цитированием
  (#4 (permalink)) Старый
Shunix Shunix вне форума
Member
 
Сообщений: 1,355
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
Регистрация: 07.06.2002
По умолчанию 26.06.2008, 12:12

Коллеги,

Спасибо за высказанные идеи и мнения!

Продолжаем разговор.
Влад, признаться, я не сразу понял "подвоха" в коде, но затем все встало на свои места. Н-да, подводный камень, который обнаружить не так-то просто. Впрочем, развивая эту же идею, фактически все опять приводится к тому же самому примеру из MSDN:
Код:
class X
{
public:

    X()
    {
    }

    virtual void foo() = 0
    {
        std::cout << "X:foo" << std::endl;
    }

    virtual ~X()
    {
    }
};

class /*__declspec( novtable )*/ Y: public X
{
public:

    virtual void foo()
    {
        X::foo();

        std::cout << "Y:foo" << std::endl;
    }
};

int main()
{
    X* x = new Y();
    x->foo();
    delete x;

    return 0;
}
Таким образом, получается, что требования к использованию данной конструкции должны быть более жесткими (нежели чем простое "that is, classes that will never be instantiated on their own").

Romendakil, что касается интерфейсных классов, то, как известно, практика объявления чисто-виртуальной функции не накладывает ограничений на отсутствие объявлений ее тела (см. класс X и соответствующее обращение к методу X::foo в теле метода Y::foo в примере выше).

Другие коллеги высказывали аналогичные мнения, которые сводятся к:
а) подобное обращение будет приводить к access violation в случае прямого или косвенного обращения к виртуальному методу (осуществляемому через таблицу виртуальных вызовов) - см. примеры выше,
б) существует давно написанный до появления этой конструкции низкоуровневый код, который может полагаться на прямое обращение и изменение таблицы виртуальных методов (это удобно, когда речь идет о подмене функций-заглушек на реальные функции, подставляемые динамически - которые, например, могут быть сгенерированы пользователем на основе некоторых правил и пр.), который не будет работать в этом случае. Конечно, не каждый пользуется подобными штуками, но они имеют место быть.
Ответить с цитированием
  (#5 (permalink)) Старый
Влад Влад вне форума
Специалист
 
Сообщений: 3,884
Сказал(а) спасибо: 1
Поблагодарили 25 раз(а) в 25 сообщениях
Регистрация: 27.06.2002
Адрес: Санкт-Петербург
По умолчанию 26.06.2008, 12:28

Вот еще ссылка на статью, в которой подробно объясняется, как работает механизм vtbl и __declspec(novtable): http://msdn.microsoft.com/ru-ru/magazine/c...398(en-us).aspx


The difference between theory and practice is that in theory, there is no difference between theory and practice, but in practice, there is.
Ответить с цитированием
Ads.
  (#6 (permalink)) Старый
girtablilu
Guest
 
Сообщений: n/a
По умолчанию 02.07.2008, 03:58

Доброй ночи!

Влад привел интересный пример, который безусловно показывает, что использование абстрактного класса c опцией __declspec(novtable) и без нее приводит к разным результатам. Но к сожалению, пример основан на некорректном подходе: вызов виртуальных функция в конструкторе и использование this несконструированного объекта.

Пример Shunix не описывает проблему, т.к. __declspec(novtable) в примере должно стоять не у класса Y, а у X (класс Y не абстрактен + мы создаем его объекты, поэтому там novtable не должно быть...) Сам пример MSDN хоть и правилен, но тоже не отражает суть проблемы, т.к. класс X там не абстрактен.

Хочу высказать предположение, что в случае отсутствия вызовов виртуальных ф-ий из конструкторов (деструкторов) нельзя придумать рабочего примера показывающего разницу между использованием абстрактного класса с опцией __declspec(novtable) и без нее. При тех же условиях думаю, что невозможна ситуация описанная в пункте а) у Shunix (т.е. получение access violation).

С уважением, girtablilu.
Ответить с цитированием
  (#7 (permalink)) Старый
Влад Влад вне форума
Специалист
 
Сообщений: 3,884
Сказал(а) спасибо: 1
Поблагодарили 25 раз(а) в 25 сообщениях
Регистрация: 27.06.2002
Адрес: Санкт-Петербург
По умолчанию 02.07.2008, 11:18

Цитата:
Но к сожалению, пример основан на некорректном подходе: вызов виртуальных функция в конструкторе и использование this несконструированного объекта.
Разумеется, я в курсе, что пример не вполне корректный и использовать this для несконструированного объекта нельзя. (Так и написано - код надуманный. Кстати, Lint бы такой код не пропустил бы.) Но, к сожалению, компилятор его "хавает"... И, к еще большему сожалению, мне приходилось встречать подобный код в реальных проектах продающихся коммерческих продуктов, - с историей порядка 10 лет и объемом около полумиллиона строк кода, писавшихся где-то примерно 4-5 поколениями программистов самой различной (как хорошей, так и разной ) квалификации.... И было бы замечательно, если бы компилятор просто отказывался бы компилировать такой код. Но - увы, это не так.

Именно в силу требования совместимости с ранее написанным кодом нельзя устанавливать __declspec(novtable) по умолчанию.
Другие мнения и дискуссии приветствуются!


The difference between theory and practice is that in theory, there is no difference between theory and practice, but in practice, there is.
Ответить с цитированием
  (#8 (permalink)) Старый
Исмаил Прокопенко Исмаил Прокопенко вне форума
Member
 
Сообщений: 23
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
Регистрация: 30.07.2016
По умолчанию 07.09.2016, 02:55

Цитата:
Сообщение от Влад Посмотреть сообщение
]
...
Можно поэкспериментировать с этим кодом, комментируя и раскомментируя строку __declspec(novtable) в BaseComputer, который, безусловно, является абстрактным классом, и убедиться, что поведение программы существенно меняется.
...
Т.е. что получается.
Что микрософт добавил в С++ какие-то нестандартные фишки?
Ответить с цитированием
Ads
Ответ

Опции темы
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Trackbacks are Вкл.
Pingbacks are Вкл.
Refbacks are Выкл.


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Кто хочет написать демон под unix за хорошее вознаграждение? belochka21 C++ на Unix 1 15.01.2012 17:43
Кто хочет написать демон под unix за хорошее вознаграждение? belochka21 Общие вопросы программирования 2 08.01.2012 14:11
Организация программ с использованием виртуальных функций и абстрактных базовых класс Natashka89 С/С++ 28 25.12.2011 18:34
Математик-программист (хорошее знание С++ или C#), Солнцево, до 90000 Net arkanoid Работа 0 18.02.2011 20:20
Хорошее охлаждение для core i7 860 edikn79 Охлаждение и разгон 3 15.02.2011 14:49
Как сделать это не хорошее дело ? ZXCV Любые вопросы от новичков 9 09.12.2010 19:57
Как снять умолчание Велий Любые вопросы от новичков 3 23.11.2010 18:18
Хорошее предложение! Vex Продам 0 12.07.2010 02:14
Кулер на проц GlacialTech Igloo 5600 хорошее решение или нет? Валера Охлаждение и разгон 2 27.05.2008 18:28
Хорошее применение старому монитору Biggi Моддинг 22 16.04.2007 14:11



Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Нardforum.ru - компьютерный форум и программирование, форум программистов