以下代码编译并正常工作,允许我访问类的受保护字段。但这是一件好事吗?感觉很脏,但我从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;
}
如果我是对的并且这不好,那么通常不需要修改对象的内部封装数据字段的好方法是什么,但是在测试中是否需要更改?实例是在其他组件的深处创建的,因此更改其类型并非易事。
答案 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}}。