Я использую размещение новых и виртуальных функций;почему моя таблица виртуальных функций неверна? - PullRequest
0 голосов
/ 04 февраля 2012

У меня есть шаблон класса, который я использую, чтобы «обернуть» несколько распространенных типов контейнеров. Цель состоит в том, чтобы заменить

getItems( vector< ItemType > &x );
getItems( set< ItemType > &x );
getItems( my_custom_vector< ItemType > &x );
(...and several others...)

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

getItems( Cont< ItemType > x );

Урезанная реализация (я буду включать только код для векторов) в настоящее время выглядит следующим образом:

template< class T > ContBase {
public:
   ContBase( void *container ) : mContainer( container ) {}
   virtual void add( const T& item ) = 0;
   virtual void clear() = 0;
protected:
   void *mContainer;
};

template< class T > ContVector : public ContBase< T > {
public:
   ContVector( std::vector< T > &native_container )
      : ContBase( (void *) &native_container ) {}
   void add( const T& item ) { vec().push_back(item); }
   void clear() { vec().clear(); }
protected:
   std::vector< T > &vec() { return *(std::vector< T > *) mContainer; }
};

template< class T > class Cont {
public:
   Cont( std::vector< T > &x ) { new (&mMem) ContVector< T >( x ); }
   void clear() { container()->clear(); }
   void add( const T& item ) { container()->add( item ); }
protected:
   ContBase< T > *container() { return (ContBase< T > *) &mMem[ 0 ]; }
   unsigned char mMem[ sizeof(ContBase< T >) ];
};

Я провел некоторое начальное тестирование, и оно отлично работало в настройке контролируемых тестов. Я думаю, что здесь главное - хитрое умное использование этого размещения, нового в конструкторе для Cont. Однако, как только я вставил его в реальное приложение (компилятор Visual Studio 2010 C ++) и запустил оптимизацию всей программы (с генерацией кода во время компоновки, оптимизацией по скорости и т. Д.), Я начал видеть серьезные проблемы. В частности, у меня есть такая функция:

void MyClass::myFunc( Index x, Cont< Index > container, uint r )
{
    // ... Return if x is invalid
    cerr << "VFPTR: " << (void*) ((uint *) ((void*) &verts))[0] << endl;
    container.add( x );
    // ...Potentially add some more things to container.
}

Который дает мне вывод, подобный этому (обратите внимание, что безумное преобразование типов - это хак, чтобы получить значение таблицы виртуальных функций в VC ++):

VFPTR: 0AD98708
// Repeat above line 17 times
VFPTR: 0018B830

Во-первых, очень странно, что указатель виртуальной функции может отличаться. Во-вторых, раздражает, что этот же код работает в режиме отладки; это отображается только при включенной оптимизации. В-третьих, следует отметить, что новый указатель (0018B830) является указателем на таблицу виртуальных функций в классе base , ContBase. Затем происходит сбой при вызове container.add (x) со значением NULL.

Итак, мой основной вопрос здесь: это юридический код, или я пропустил случай неопределенного поведения? Здесь не работает компилятор Microsoft или мой код не определен для начала?

1 Ответ

0 голосов
/ 04 февраля 2012

Основная проблема с неопределенным поведением, которую я вижу, заключается в том, что (по умолчанию) конструктор копирования и оператор присваивания Cont<T> делают побайтную копию объекта ContVector<T>, содержащегося в массиве mMem.Поскольку ContVector<T> не является тривиально копируемым (см. 9 и 3.9 в спецификации C ++), это не определено.Из вашего описания точной проблемы, которую вы видите, кажется, что оптимизатор делает предположение, что он может переупорядочить некоторую операцию с байтовой копией массива mMem, в результате чего вы получите копию частично сконструированного (или частично разрушенного) объекта.

Таким образом, вы можете выполнить эту работу, добавив ctor и оператор присваивания, которые выполняют копирование / присваивание правильно, а не полагаясь на побочную копию массива mMem.

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