我很确定这是危险的代码。但是,我想查看是否有人知道 究竟会出错。
假设我有这个类结构:
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)上工作正常,但这当然不能保证它不会对内存做坏事和/或不会在另一个编译器/机器上完全失败。
答案 0 :(得分:5)
删除指针时将调用虚析构函数Base::~Base
。由于B
没有正确的vtable(在此处发布的代码中根本没有),因此不会很好地结束。
它只适用于这种情况,因为你有内存泄漏,你永远不会删除test
。
答案 1 :(得分:1)
您的代码会产生未定义的行为,因为它违反了严格的别名。即使它没有,你也在调用UB,因为B和A都不是多态类,并且指向的对象不是多态类,因此dynamic_cast
不能成功。您正在尝试访问不存在的Base对象,以便在使用dynamic_cast
时确定运行时类型。
我能做出的一个非常重要的保证是既不是基地 也不会有任何其他成员变量或方法。他们是 基本上是空的。
它根本不重要 - 它完全无关紧要。该标准必须要求EBO才能开始重要,但事实并非如此。
答案 2 :(得分:-1)
只要您不对Test<B>*
执行任何操作并避免使用智能指针或自动内存管理等任何魔法,您应该没问题。
您应该确保查找隐藏的代码,例如调试打印或将检查对象的日志记录。我试图查看像这样设置的指针的值,让调试器崩溃了。我敢打赌这会给你带来一些痛苦,但你应该能够让它发挥作用。
我认为真正的问题是维护。在某个开发人员对Test<B>*
执行操作之前需要多长时间?