"动态"改变现有对象的VTBL,动态子类化

c-s*_*ile 6 c++ new-operator c++11

请考虑以下设置.

基类:

class Thing {
  int f1;
  int f2;

  Thing(NO_INIT) {}
  Thing(int n1 = 0, int n2 = 0): f1(n1),f2(n2) {}
  virtual ~Thing() {}

  virtual void doAction1() {}
  virtual const char* type_name() { return "Thing"; }
}
Run Code Online (Sandbox Code Playgroud)

和派生类只有通过上述方法的实现才有所不同:

class Summator {
  Summator(NO_INIT):Thing(NO_INIT) {}

  virtual void doAction1() override { f1 += f2; }
  virtual const char* type_name() override { return "Summator"; }
}

class Substractor {
  Substractor(NO_INIT):Thing(NO_INIT) {}    
  virtual void doAction1() override { f1 -= f2; }
  virtual const char* type_name() override { return "Substractor"; }
}
Run Code Online (Sandbox Code Playgroud)

我的任务需要能够动态更改现有对象的类(在本例中为VTBL).如果我没有弄错,这就称为动态子类化.

所以我提出了以下功能:

// marker used in inplace CTORs
struct NO_INIT {}; 

template <typename TO_T>
    inline TO_T* turn_thing_to(Thing* p) 
    { 
      return ::new(p) TO_T(NO_INIT()); 
    }
Run Code Online (Sandbox Code Playgroud)

就是这样 - 它使用inplace new来构造一个对象来代替另一个对象.实际上,这只会更改对象中的vtbl指针.所以这段代码按预期工作:

Thing* thing = new Thing();
cout << thing->type_name() << endl; // "Thing"
turn_thing_to<Summator>(thing);
cout << thing->type_name() << endl; // "Summator"
turn_thing_to<Substractor>(thing);
cout << thing->type_name() << endl; // "Substractor"
Run Code Online (Sandbox Code Playgroud)

我对这种方法唯一的主要问题是:a)每个派生类都应该有类似的特殊构造函数Thing(NO_INIT) {}.并且b)如果我想要将诸如std :: string之类的成员添加到Thing中它们将无法工作 - 只允许具有NO_INIT构造函数的类型作为Thing的成员.

问题:对于解决'a'和'b'问题的动态子类化是否有更好的解决方案?我有一种感觉,std :: move语义可能有助于以某种方式解决'b'但不确定.

这是代码的构思.

Nic*_*kin 2

(已在 RSDN 上回答http://rsdn.ru/forum/cpp/5437990.1

有一个棘手的方法:

struct Base
{
    int x, y, z;
    Base(int i) : x(i), y(i+i), z(i*i) {}
    virtual void whoami() { printf("%p base %d %d %d\n", this, x, y, z); }
};

struct Derived : Base
{
    Derived(Base&& b) : Base(b) {}
    virtual void whoami() { printf("%p derived %d %d %d\n", this, x, y, z); }
};

int main()
{
    Base b(3);
    Base* p = &b;

    b.whoami();
    p->whoami();

    assert(sizeof(Base)==sizeof(Derived));
    Base t(std::move(b));
    Derived* d = new(&b)Derived(std::move(t));

    printf("-----\n");
    b.whoami(); // the compiler still believes it is Base, and calls Base::whoami
    p->whoami(); // here it calls virtual function, that is, Derived::whoami
    d->whoami();
};
Run Code Online (Sandbox Code Playgroud)

当然是UB了。