2011-01-12 4 views
8

Вот пример кода, который меня раздражает:Как получить доступ к защищенному методу в базовом классе из производного класса?

class Base { 
    protected: 
    virtual void foo() = 0; 
}; 

class Derived : public Base { 
    private: 
    Base *b; /* Initialized by constructor, not shown here 
       Intended to store a pointer on an instance of any derived class of Base */ 

    protected: 
    virtual void foo() { /* Some implementation */ }; 
    virtual void foo2() { 
     this->b->foo(); /* Compilator sets an error: 'virtual void Base::foo() is protected' */ 
    } 
}; 

Как получить доступ к защищенной перекрытой функции?

Благодарим за помощь. : o)

+7

Я не думаю, что ваша реализация совершенно правильная. Почему у вас есть экземпляр Base в качестве переменной-члена? this-> b-> foo() будет пытаться вызвать чистый виртуальный метод. – GWW

+1

Эта программа не должна компилироваться. Вы не можете создать экземпляр абстрактного класса ... Если 'b' указывает на экземпляр какого-либо другого класса, полученного из' Base'. – 341008

+0

Я опустил точность: атрибут Derived :: b предназначен для хранения любого экземпляра производных классов из Base –

ответ

8

Защищенные элементы в базовом классе доступны только текущему объекту.
Таким образом, вы можете позвонить по номеру this->foo(), но вы не можете позвонить по номеру this->b->foo(). Это не зависит от того, реализует ли Derived реализацию для foo или нет.

Причина этого ограничения заключается в том, что в противном случае было бы очень легко обойти защищенный доступ. Вы просто создаете класс, например Derived, и вдруг у вас также есть доступ к частям других классов (например, OtherDerived), которые должны были быть недоступны для посторонних.

+0

Спасибо, я теперь ясно понимаю причины ограничения ... Это будет дыра в безопасности ... большая! –

+5

Пожалуйста, не думайте об этом как о дыре в безопасности. Модификаторы доступа не обеспечивают никакой безопасности, вы можете просто прочитать ячейку памяти, если хотите получить данные. – DrYap

5

Обычно вы должны сделать это с использованием Base::foo(), который относится к базовому классу текущего экземпляра.

Однако, если ваш код должен сделать это так, как вы его пытаетесь, и это не разрешено, вам нужно либо сделать foo() public, либо сделать Derived другом Base.

0

Вы вызываете базовые функции явно с помощью оператора области (Base :: foo()). Но в этом случае базовый класс не определяет foo (это чистый виртуальный), поэтому на самом деле нет функции для выполнения, когда вы говорите this->b->foo();, поскольку b является указателем на Base и not Derived.

+1

Но код OP не относится к базовому классу текущего экземпляра. Он обращается к другому экземпляру, который предположительно является производным классом, реализующим чисто виртуальную функцию. (В противном случае экземпляр не может быть создан.) –

+0

@ Jonathan Wood Я понимаю, что вы говорите, но, просто переходя от кода, который он опубликовал, похоже, что он пытается создать абстрактный базовый класс (Base) и называть чистым виртуальная функция (Base :: foo()), которая является no-no (как GWW и 341008, упомянутые выше). – Gemini14

0

Как вы можете получить доступ к защищенной функции ?

--- откуда?

Вы можете получить доступ к защищенному члену только через наследование (кроме методов того же класса). Например, у вас есть class Derived1, который наследуется от Derived, а затем объекты Derived1 могут звонить по номеру foo().

EDIT: MSDN article по спецификатору защищенного доступа.

1

Немного хрупкий, но с классами, которые вы определили здесь, не будет ли это работать?

virtual void foo2() { 
    reinterpret_cast<Derived *>(this->b)->foo(); 
} 

Точки reinterpret_cast на виртуальные таблицы для базового объекта, и называет его через это члены аксессору.

2

Одним из решений было бы объявить статическую защищенную функцию в Base, которая перенаправляет вызов частной/защищенной функции (foo в примере).

Позволяет сказать:

class Base { 
protected: 
    static void call_foo(Base* base) { base->foo(); } 
private: 
    virtual void foo() = 0; 
}; 

class Derived : public Base { 
private: 
    Base* b; 
protected: 
    virtual void foo(){/* Some implementation */}; 
    virtual void foo2() 
    { 
     // b->foo(); // doesn't work 
     call_foo(b); // works 
    } 
}; 

Таким образом, мы не нарушает инкапсуляцию, потому что дизайнер Base может сделать явный выбор, чтобы все производные классы называть foo друг на друга, избегая при этом ставить foo в открытый интерфейс или в явном переводе всех подклассов Base в друзей.

Кроме того, этот метод работает независимо от того, является ли он foo виртуальным или нет, или является ли он конфиденциальным или защищенным.

Here - это ссылка на бегущую версию вышеприведенного кода и here другую версию той же идеи с немного большей логикой ведения бизнеса.