Доступ к указателю на защищенный метод? - PullRequest
13 голосов
/ 28 апреля 2011

Этот код:

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

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

Ответы [ 6 ]

20 голосов
/ 28 апреля 2011

Вы можете взять адрес через 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

5 голосов
/ 28 апреля 2011

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

3 голосов
/ 28 апреля 2011

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

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

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

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


В качестве резюме прочитайте отчет об этой проблеме .

3 голосов
/ 28 апреля 2011

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

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

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

1 голос
/ 28 апреля 2011

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

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);

  }
}
0 голосов
/ 28 апреля 2011

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

Да, с идиомой пароля . :)

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 .

...