我正在使用新的& amp;虚函数;为什么我的虚函数表错了?

时间:2012-02-03 21:45:22

标签: c++ visual-c++

我有一个类模板,我用来“包装”几种常见类型的容器。目标是取代

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 ++中获取虚函数表的值的一个hack):

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

首先,确实虚拟函数指针可以不同似乎很奇怪。其次,令人讨厌的是,这个相同的代码在调试模式下工作;这仅在启用优化时显示。第三,值得注意的是,新指针(0018B830)是指向 base 类ContBase中的虚函数表的指针。当您调用container.add(x)时,会发生崩溃,这是一个NULL值。

所以我的核心问题是:这是合法代码,还是我错过了一个未定义的行为案例? Microsoft的编译器在这里被破坏了,还是我的代码未定义为开始?

1 个答案:

答案 0 :(得分:0)

我看到的主要未定义行为问题是Cont<T>的(默认)复制构造函数和赋值运算符执行mMem数组中保存的ContVector<T>对象的逐字节副本。由于ContVector<T>不是可复制的(参见C ++规范中的9和3.9),因此未定义。根据您对确切问题的描述,似乎优化器假设它可以使用mMem数组的字节副本重新排序某些操作,这会导致您获得部分构造(或部分破坏)对象的副本。 / p>

所以你可以通过添加一个复制ctor和赋值运算符来正常复制/分配而不是依赖于mMem数组的逐字副本来完成这项工作。