c++危险铸造代码



我确信这是一个危险的代码。然而,我想检查一下是否有人知道到底会出什么问题。

假设我有这样的类结构:

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 {};

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

(潜在)危险代码如下:

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)上运行良好,尽管这当然不能保证它不会对内存造成不良影响和/或在另一台编译器/机器上不会完全失败。

删除指针时将调用虚拟析构函数Base::~Base。由于B没有合适的vtable(在这里发布的代码中根本没有),所以结果不会很好。

它只在这种情况下工作,因为您有内存泄漏,您永远不会删除test

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

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

这一点都不重要,完全无关。《标准》必须授权欧洲广播公司,这一点才能开始发挥作用,但事实并非如此。

只要你不使用Test<B>*执行任何操作,并且避免任何像智能指针或自动内存管理这样的魔术,你就应该没事了。

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

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

最新更新