Доступ к методу указателя на защищенный метод?

Этот код:

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
  • Почему я могу вызвать защищенный метод, но не принимать его адрес?
  • Есть ли способ отметить что-то полностью доступное из производных классов, а не только доступное из производных классов и в отношении указанного производного класса?

BTW: Это связано с, но я ищу ссылку на то, где это вызывается в спецификации или тому подобное (и, надеюсь, это приведет к как заставить вещи работать так, как я ожидал).

Ответы

Ответ 1

Вы можете взять адрес через D, написав &D::Foo вместо &B::Foo.

См. этот компилятор отлично: http://www.ideone.com/22bM4

Но это не скомпилируется (ваш код): http://www.ideone.com/OpxUy


Почему я могу вызвать защищенный метод, но не принимать его адрес?

Вы не можете взять свой адрес, написав &B::Foo, потому что Foo является защищенным членом, вы не можете получить к нему доступ извне B, даже не его адрес. Но записывая &D::Foo, вы можете, потому что Foo становится членом D через наследование, и вы можете получить его адрес, независимо от того, является ли его конфиденциальным, защищенным или общедоступным.

&B::Foo имеет такое же ограничение, как b.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

Ответ 2

Это связано с тем, что объект производного класса может обращаться только к защищенным членам базового класса, если он является одним и тем же объектом. Разрешение на использование указателя защищенной функции-члена сделает невозможным сохранение этого ограничения, поскольку указатели на функции не содержат никакой информации с ними.

Ответ 3

Я считаю, что protected работает не так, как вы думаете, на С++. В С++ protected разрешается только доступ к родительским членам собственного экземпляра НЕ произвольным экземплярам родительского класса. Как отмечалось в других ответах, обращение адреса родительской функции нарушило бы это.

Если вы хотите получить доступ к произвольным экземплярам родителя, у вас может быть родительский класс friend или его родительский метод public. Невозможно изменить значение protected, чтобы сделать то, что вы хотите, чтобы он делал в программе на С++.

Но что вы на самом деле пытаетесь сделать здесь? Возможно, мы сможем решить эту проблему для вас.

Ответ 4

Почему я могу вызвать защищенный метод, но не принимать его адрес?

У этого вопроса есть ошибка. Вы не можете позвонить либо

B *self = this;
self->Foo(); // error either!

Как говорится в другом ответе, если вы получаете доступ к нестационарному защищенному элементу с помощью D, тогда вы можете. Может быть, вы хотите прочитать этот?


В качестве сводки прочитайте этот отчет.

Ответ 5

Ваше сообщение не отвечает "Почему я могу вызывать защищенный метод, но не принимать его адрес?"

class D : public B {
 public:
  void Baz() {
    // this line
    Foo();
    // is shorthand for:
    this->Foo();
  }
  void Bar() {
    // this line isn't, it taking the address of B::Foo
    printf("%x\n", &B::Foo);

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

  }
}

Ответ 6

Есть ли способ отметить что-то полностью доступное из производных классов, а не только доступное из производных классов и в отношении указанного производного класса?

Да, с идентификатором 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
}

См. полный пример в Ideone.