是否可以滥用reinterpret_cast将对象转换为派生类?

时间:2017-02-03 19:01:31

标签: c++ testing

以下代码编译并正常工作,允许我访问类的受保护字段。但这是一件好事吗?感觉很脏,但我从Java知道的是什么:

#include <iostream>

class Base {
  public:
    Base() : _f(42) {
    }
    int getF() { return _f; }
  protected:
    int _f;
};

class Der : public Base {
  public:
    void setF(int f) { _f = f; }
};

int main(int argc, char ** argv) {
  Base *b = new Base();
  std::cout << b->getF() << std::endl;
  Der *d = reinterpret_cast<Der*>(b);
  d->setF(37);
  std::cout << b->getF()<< std::endl;
}

如果我是对的并且这不好,那么通常不需要修改对象的内部封装数据字段的好方法是什么,但是在测试中是否需要更改?实例是在其他组件的深处创建的,因此更改其类型并非易事。

1 个答案:

答案 0 :(得分:4)

不,如果您假设某个对象的类型为Der*,则该行为未定义。这可能在运行时失败,而且很糟糕。除了通常的(特别是请求你的编译器使代码无效崩溃)之外,在运行时可能失败的一种方法是,如果你的编译器的优化器假设你将它转换为Der,它必须真的是Der类型1}}。如果您通过调用虚函数来执行此操作,则编译器可能会认为由于动态类型已知为friend,因此可以优化虚拟方法查找。

  

如果我是对的并且这不好,那么通常不需要修改的对象的内部封装数据字段有什么好方法,但是在测试中需要更改?

Base关键字似乎适合。此关键字使类的私有(和受保护)成员在类外部可用。由于您的class Base { friend class BaseTester; // ... protected: int _f; }; class BaseTester { public: static void test() { Base *b = new Base(); b->_f = 37; } }; int main(int argc, char ** argv) { BaseTester::test(); } 类应该知道哪个类或函数将对其进行单元测试,因此它可以授予对该类或函数的访问权。

Base

为了完整起见,C ++的访问检查中存在一些漏洞,如果由于某种原因您无法修改#include <iostream> class Base { // ... protected: int _f; }; class BaseHack : public Base { public: static constexpr int Base::*_f = &BaseHack::_f; }; int main(int argc, char ** argv) { Base *b = new Base(); b->*BaseHack::_f = 37; } ,则可能会滥用这些漏洞。这是一个:

BaseHack

&BaseHack::_f内,允许使用表达式_f,因为它命名了一个受保护的基类成员,通过它自己的类访问。但由于Base实际上是在int Base::*类中定义的,因此其类型为int BaseHack::*而不是Base,并且没有规则阻止它被用于访问{{1}的成员1}}。