0

我很确定这是危险的代码。但是,我想检查一下是否有人知道究竟会出现什么问题

假设我有这个类结构:

class A {
protected:
  int a;
public:
  A() { a = 0; }        
  int getA() { return a; }
  void setA(int v) { a = v; }
};

class B: public A {
protected:
  int b;
public:
  B() { b = 0; }
};

然后假设我想要一种自动扩展类的方法,如下所示:

class Base {
public:
   virtual ~Base() {}
};

template <typename T>
class Test: public T, public Base {};

我可以做出的一个非常重要的保证是,既没有Base也不Test会有任何其他成员变量或方法。它们本质上是空类。

(潜在的)危险代码如下:

int main() {
  B *b = new B();

  // dangerous part?
  // forcing Test<B> to point to to an address of type B
  Test<B> *test = static_cast<Test<B> *>(b);

  //
  A *a = dynamic_cast<A *>(test);
  a->setA(10);
  std::cout << "result: " << a->getA() << std::endl;
}

做这样的事情的基本原理是我正在使用一个类似于 Test 的类,但是为了让它当前工作,必须创建一个新的实例 T(即 Test),同时复制传递的实例。如果我可以将 Test 指向 T 的内存地址,那就太好了。

如果 Base 没有添加虚拟析构函数,并且由于 Test 没有添加任何内容,我认为这段代码实际上是可以的。但是,添加虚拟析构函数让我担心类型信息可能会添加到类中。如果是这种情况,那么它可能会导致内存访问冲突。

最后,我可以说这段代码在我的计算机/编译器(clang)上运行良好,尽管这当然不能保证它不会对内存造成坏事和/或不会在另一个编译器/机器上完全失败。

4

3 回答 3

5

Base::~Base删除指针时将调用虚拟析构函数。由于B没有正确的 vtable(此处发布的代码中根本没有),因此不会很好地结束。

它仅在这种情况下有效,因为您有内存泄漏,您永远不会删除test.

于 2012-12-10T23:23:47.350 回答
1

您的代码会产生未定义的行为,因为它违反了严格的别名。即使没有,您也在调用 UB,因为 B 和 A 都不是多态类,并且指向的对象不是多态类,因此dynamic_cast无法成功。您正在尝试访问不存在的 Base 对象以确定使用时的运行时类型dynamic_cast

我可以做出的一个非常重要的保证是 Base 和 Test 都不会有任何其他成员变量或方法。它们本质上是空类。

这一点都不重要——完全无关紧要。该标准必须强制 EBO 才能使这一点开始变得重要,但事实并非如此。

于 2012-12-10T23:26:50.917 回答
-1

只要您不执行任何操作Test<B>*并避免任何魔术,例如智能指针或自动内存管理,就可以了。

您应该确保查找将检查对象的模糊代码,例如调试打印或日志记录。我让调试器崩溃,因为我试图查看像这样设置的指针的值。我敢打赌这会给你带来一些痛苦,但你应该能够让它发挥作用。

我认为真正的问题是维护。一些开发人员需要多长时间才能进行操作Test<B>*

于 2012-12-10T23:07:15.053 回答