访问受保护方法的方法指针?

15
这段代码:
class B {
 protected:
  void Foo(){}
}

class D : public B {
 public:
  void Baz() {
    Foo();
  }
  void Bar() {
    printf("%x\n", &B::Foo);
  }
}

产生这个错误:

t.cpp: In member function 'void D::Bar()':
Line 3: error: 'void B::Foo()' is protected
  • 为什么我可以调用一个protected方法,但不能获取它的地址?
  • 是否有一种方法可以将某些内容标记为完全可从派生类访问,而不仅仅是可从派生类和相关派生类访问?

顺便说一下:这看起来与此相关,但我正在寻找在规范或类似文档中对此进行说明的参考资料(希望这将有助于解决如何使事情按照我的预期工作的问题)。

7个回答

23
你可以通过写 &D::Foo 来获取地址,而不是 &B::Foo。查看此示例可以编译成功:http://www.ideone.com/22bM4,但你的代码无法编译成功:http://www.ideone.com/OpxUy 为什么我能够调用一个受保护的方法但却不能取它的地址?
因为 Foo 是一个受保护成员,在 B 类外部无法访问,包括其地址。但是你可以通过写 &D::Foo 来获取其地址,因为通过继承,Foo 成为了 D 类的成员,无论它是私有的、受保护的还是公共的,你都可以获取其地址。
在以下代码中,&B::Foob.Foo()pB->Foo() 有相同的限制:
void Bar() {
    B b;
    b.Foo();     //error - cannot access protected member!
    B *pB = this;
    pB->Foo();   //error - cannot access protected member!
  }

查看错误:在ideone上http://www.ideone.com/P26JT


你的帖子没有回答“为什么我可以调用受保护的方法,但不能取其地址?” - Prasoon Saurav
由于原因过于复杂,我不想在这个时候(词法上)引用派生类。 - BCS
3
@Prasoon说:“人们希望张贴者发布完整的答案”...我会说“这是一个好期望”。但我回答的范围仅限于我所知道的。 - Nawaz
1
@Prasoon:不完整的回答加一。{ 戈德温定律::调用(); } - BCS
1
由于某些原因,我之前不知道这是可能的。很棒!所以,除了所有人对完整性的抱怨外,你的答案在你进行任何编辑之前就已经告诉了我一些有用的内容。 - underscore_d
显示剩余5条评论

6
这是因为一个派生类的对象只能访问基类的受保护成员,如果它是同一个对象。允许您获取受保护成员函数的指针将使得维护此限制变得不可能,因为函数指针并不携带任何此信息。

我不想有那个限制。有没有办法让事情按照我预期的方式工作?(请参见编辑) - BCS
@BCS我不太确定为什么一开始就有同一对象的限制。了解你想要实现什么会很有帮助。 - hammar
@BCS 实际上不行,因为你所要求的是对 C++ 标准的更改。你可以分叉一个 C++ 编译器并修改它以允许这样的结构,但那将不再是 C++ 了。 - Mark B
1
如果您被允许做您想做的事情,那么 protected 关键字将几乎没有用处,因为任何人都可以通过从它派生一个虚拟类并创建一个指向成员的指针来访问其他类的受保护部分。这是按设计不起作用的! - Bo Persson

3
我相信在C++中,protected并不像你想象的那样工作。在C++中,protected仅允许访问其自身实例的父成员,而不是父类的任意实例。正如其他答案所指出的,获取父函数的地址将违反此规定。
如果您想访问父类的任意实例,则可以使父类成为子类的友元,或将父方法设置为public。在C++程序中,没有办法改变protected的含义以执行您想要的操作。
但是,您真正想要做什么?也许我们可以帮您解决该问题。

1
将事物公开使得非派生类可以访问(不好)。使用“友元”需要B的作者预先知道所有派生类(不好),并且还会给他们访问不应该访问的东西的权限(不好)。 - BCS

3
为什么我可以调用受保护的方法但不能取其地址?
这个问题本身存在错误。你也无法进行调用。
B *self = this;
self->Foo(); // error either!

作为另一篇回答所说,如果您通过 D 访问非静态受保护成员,那么您可以这样做。也许你想阅读 这个
简而言之,请阅读这个问题报告

1
你的帖子没有回答“为什么我可以调用受保护的方法,但不能取其地址?”
class D : public B {
 public:
  void Baz() {
    // this line
    Foo();
    // is shorthand for:
    this->Foo();
  }
  void Bar() {
    // this line isn't, it's taking the address of B::Foo
    printf("%x\n", &B::Foo);

    // not D:Foo, which would work
    printf("%x\n", &D::Foo);

  }
}

从语言纯粹主义的角度来看,这可能是真实/相关的,但事实上在机器级别上,函数F::FooD::Foo是相同的东西(证明:派生类可以调用受保护的基类方法,即使跨越不同的编译)。 - BCS
@BCS - 是的,但是只有在你实际拥有一个D对象时,你才能访问D::Foo。你不能使用该指针来访问任何其他类的成员Foo。这是有意为之的! - Bo Persson
我的断言是 D::Foo 就是 B::Foo(在底层),所以在这种情况下,我有两种方式来完成完全相同的事情,但只有一种是合法的。如果你从 CPU 实际发生的情况来看,这似乎非常牵强。实际上,我在抱怨 C++ 选择作为其方法引用的内容不够低级。 - BCS

0

0
有没有一种方法可以将某些东西标记为完全可从派生类访问,而不仅仅是可从派生类访问并与该派生类相关?
是的,使用 passkey习语。 :)
class derived_key
{
    // Both private.
    friend class derived;

    derived_key() {}
};

class base
{
public:
    void foo(derived_key) {}
};

class derived : public base
{
public:
    void bar() { foo(derived_key()); }
};

由于只有derived可以访问derived_key的构造函数,因此只有该类可以调用foo方法,即使它是公共的。
这种方法的明显问题是需要为每个可能的派生类添加友元,这样很容易出错。另一种可能的(在您的情况下更好的)方法是将基类设置为友元,并公开一个受保护的get_key方法。

class base_key
{
    friend class base;

    base_key() {}
};

class base
{
public:
    void foo(base_key) {}

protected:
    base_key get_key() const { return base_key(); }
};

class derived1 : public base
{
public:
    void bar() { foo(get_key()); }
};

class derived2 : public base
{
public:
    void baz() { foo(get_key()); }
};

int main()
{
  derived1 d1;
  d1.bar(); // works
  d1.foo(base_key()); // error: base_key ctor inaccessible
  d1.foo(d1.get_key()); // error: get_key inaccessible

  derived2 d2;
  d2.baz(); // works again
}

查看完整示例{{link1:在Ideone上}}。


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