通过这个技巧从外部访问受保护的成员,但这有效吗?

4
如果我有以下类:
class Foo
{
protected:
    int i;
public:
    Foo() : i(42) {}
};

当然,我在外部无法访问受保护的成员,但我可以使用这个小技巧:首先创建一个继承Foo的新类:

class Foo2 : public Foo
{
public:
    int GetI() { return i; }
};

现在,每当我拥有 Foo 的实例或指向这样的实例的指针时,我可以通过转换访问受保护的成员(因为我不使用任何额外的成员):

Foo *f = new Foo();
Foo f2;
std::cout << ((Foo2*)f)->GetI() << std::endl;
std::cout << (reinterpret_cast<Foo2&>(f2)).GetI() << std::endl;

我理解这个代码为什么能够运行,但是是否会有不良影响呢?编译器没有问题,也没有运行时检查。
2个回答

5
reinterpret_cast<Foo2&>(f2)).GetI()

从技术上讲,这是未定义行为。因此,它可能有效,但不一定如此。


1
不知道这个,但指针技巧还能使用吗? - Toni Petrina
3
对我来说,更重要的问题是为什么你需要这个技巧?访问规范不是用来愚弄编译器的,而是一种实现可维护和设计良好代码的手段,维护代码的是人而不是编译器。 - Alok Save
@AlokSave 我的同事需要它来解决一些 MFC 的 bug。我只是觉得很有趣它能够工作。 - Toni Petrina
@DrewDormann 但这样会创建一个原始对象的副本,如果我想修改原始对象,这不是一个好的解决方案。 - Toni Petrina
1
尽管在一般情况下是正确的,但我认为这个简单的例子涉及到了一个特殊的例外。它是一种标准布局类型(因为它缺少虚拟函数,并且派生类没有添加新数据,所有数据都具有相同的访问权限等)。所以,总体上你是正确的,但这个简单的例子可能是一个例外,因为它符合标准布局类型的规则。 - edA-qa mort-ora-y
显示剩余7条评论

1
您正在将一个Foo对象向下转换为Foo2对象。
下转换是从基类到派生类的转换。仅当在运行时所寻址的对象实际上寻址的是派生类对象时,下转换才是安全的。
为了保护您的代码,必须使用dynamic_cast来检查下转换是否有效。
不建议使用reinterpret_cast进行下转换。请使用static_castdynamic_cast
阅读一些文章,许多人像您一样写道“不要使用下转换”。一个危险的例子是在Foo中有一个virtual void GetI()

“不推荐使用reinterpret_cast是因为其行为依赖于编译器”这种说法非常误导人,甚至可以说是错误的。标准在使用reinterpret_cast时提供了某些保证,就像语言中的任何其他结构一样。如果您超出了这些边界,那么您将得到未定义的行为。这不是推荐/不推荐使用reinterpret_cast的原因。” - Alok Save
我知道使用动态转换将防止我这样做。您知道使用上述技巧可能会遇到什么问题吗? - Toni Petrina

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接