downcasting — возможно ли уменьшить рейтинг объекта до подкласса, который не определяет дополнительную переменную или vtable в C ++?

Возможно ли преобразовать объект в подкласс, не определяя дополнительную переменную или виртуальный метод?

Если у меня есть эти классы,

class A { public: A (); };
class B : public A { public: void method1 () {} B (); };

возможно ли это (1) и (2) безопасно по стандарту?

A* a = new A ();
B* b = (B*)a;
b->method1();

9

Решение

Преобразование указателя действует как static_cast, 5.2.9 / 2 говорит,

Если объект типа [A] на самом деле подобъект объекта
тип [B], результат относится к включающему объекту типа [B].
В противном случае результат приведения не определен.

Ваш объект не подобъект B объект, поэтому результат не определен.

Даже если бы вы reinterpret_castдоступ к значению объекта через результирующий указатель имеет неопределенное поведение, поскольку нарушает строгое псевдонимы. В стандарте C ++ 11, 3.10 / 10:

Если программа пытается получить доступ к сохраненному значению объекта через
поведение, отличное от одного из следующих типов поведения
недеформированные определенно:

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

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

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

После определения классы C ++ закрываются для новых членов, включая новые функции-члены. Итак, ваш объект создан с new A() имеет все функции-члены, которые он когда-либо будет иметь. Просто напишите функцию, не являющуюся членом — если только A имеет protected Участники будут иметь точно такой же доступ к A что ваша функция-член имеет. И если A имеет protected Члены тогда есть одобренный способ получения из него, который вы должны использовать для создания надлежащих экземпляров B,

Если синтаксис функции-члена так много значит для вас, то в зависимости от класса A Вы могли бы написать:

B b = *a;    // "copy" the object (give B a suitable ctor)
b.method1(); // act on it
*a = b;      // copy it back (A needs copy assignment operator)

Очевидно, что здесь есть проблемы, которые могут остановить его работу: для начала, является ли объект копируемым, а также потокобезопасным, и method1 хранит указатель / ссылку на b где-то, что начнет болтаться, как только b уничтожен В C ++ 11 копии, возможно, могут быть перемещены для повышения эффективности, но даже в этом случае я надеюсь, что вы согласитесь с тем, что вам нужно пройти через циклы, чтобы использовать синтаксис функции-члена не стоит того.

6

Другие решения

B * b = static_cast< B *> или то, что вы пытались это небезопасный удручающий,он назначает адрес объекта базового класса (A) указателю производного класса (B). Так что если вы получите доступ к чему-либо через этот указатель, это вызовет неопределенное поведение.

Давайте предположим одну возможную компоновку объекта экземпляра A:

a ->|vptr|

Когда вы выполняете принудительное приведение:

b ->|vptr|

это определенно небезопасно, поскольку a не указывает на экземпляр B или подкласс B. Когда вы вызываете виртуальный метод или изменяете поле (не в этом случае), это вызовет неопределенное поведение в общем или ошибка в этом макете.

Однако ваш метод1 не является виртуальным, поэтому нет необходимости искать виртуальную таблицу. Поскольку ваша реализация метода method1 не делает и даже не может ничего делать с этим, поэтому, когда вы запустите код в этой гипотетической компоновке объекта, он, вероятно, не сообщит об ошибке (см. Комментарий Джеймса).

4

  1. Да, это возможно.
  2. Нет, это не безопасно в соответствии со стандартом; это не рекомендуется.

Это больше похоже на приведение к C void *, что не является лучшим вариантом в C ++. Но я делал это много раз, и это прекрасно работает.

3

Вы должны прочитать этот чрезвычайно хорошо написанный пост:

Обычное приведение против static_cast против dynamic_cast

Если вы используете «static_cast», имейте в виду, что вы «принудительно» конвертируете, и это небезопасно.

1
По вопросам рекламы [email protected]