2012-04-27 4 views
5

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

class Base 
{ 
public: 
    virtual void Foo() {} 
}; 

class Derived : public Base 
{ 
private: 
    void Foo() {} 
}; 

void func() 
{ 
    Base* a = new Derived; 
    a->Foo(); //fine, calls Derived::Foo() 

    Derived* b = new Derived; 
// b->Foo(); //error 
    static_cast<Base*>(b)->Foo(); //fine, calls Derived::Foo() 
} 

Я слышал две различные школы мысли по этому вопросу:

1) Оставить ДОСТУПНОСТИ такой же, как базовый класс, так как пользователи могут использовать static_cast, чтобы получить доступ так или иначе.

2) Сделать функции максимально закрытыми. Если пользователям требуется a-> Foo(), но не b-> Foo(), то Derived :: Foo должен быть закрытым. Его всегда можно обнародовать, если и когда это необходимо.

Есть ли причина предпочесть тот или иной?

+1

Этот дизайн очень противоречит интуитивно понятным причинам. Я бы посоветовал это, если вы не встретите сценарий, который может быть разрешен только таким образом. –

+0

Если ваше намерение ограничить использование _direct_ производного класса (например, заводского шаблона), то защищенный или закрытый _inheritance_ является более подходящим способом (вместо ограничения определенных методов) – user396672

ответ

6

Ограничение доступа к элементу в подтипе нарушает Liskov substitution principle (L в SOLID). Я бы советовал против этого в целом.

Update: Это может «работать», как в коде компилируется и работает и выдает ожидаемый выход, но если вы скрываете член вашей намерения вероятно делает подтип менее общим, чем оригинал. Вот что нарушает принцип. Если вместо этого вы намерены очистить интерфейс подтипа, оставив только то, что интересно пользователю API, сделайте это.

+2

Я не думаю, что это нарушает принцип подстановки. Вы все равно можете использовать ссылку на 'Derived' в любом месте, где требуется ссылка на' Base', и она будет работать нормально. –

+0

@ BjörnPollex Я собирался ответить комментарием, но потом понял, что ответ принесет пользу от обновления. – Joni

+0

Разве какие-либо компиляторы отмечают это как предупреждение? (Еще одна точка данных) – Jon

1

Это зависит от вашего дизайна, независимо от того, хотите ли вы получить доступ к функции virtual с производным классом или нет.
Если нет, то всегда лучше сделать их private или protected.

Существует не разница в исполнении кода на основе спецификации доступа, но код становится более чистым.
Как только вы ограничили доступ к функции класса virtual; читатель этого class может быть уверен, что это не будет вызвано каким-либо объектом или указателем производного класса.

3

не ответ на оригинальный вопрос, но если мы говорим о дизайне классов ...

Как Alexandrescu и Sutter рекомендуют в их 39-е правила, следует предпочесть использовать общественные невиртуальные функции и частный/защищенный виртуальный:

class Base { 
public: 
    void Foo(); // fixed API function 

private: 
    virtual void FooImpl(); // implementation details 
}; 

class Derived { 
private: 
    virtual void FooImpl(); // special implementation 
}; 
+0

Вы можете разумно поставить реализацию 'Base :: Foo' прямо в определении класса,' void Foo() {FooImpl(); } '. Это никогда не будет более чем предсказуемым одним лайнером, вот в чем дело. –

+0

@SteveJessop Согласитесь, если FooImpl() на самом деле является просто выполнением для Foo. В реальной жизни это может быть частью алгоритма Foo с фиксированной структурой, но с некоторыми плавающими деталями. И иногда, стандарты корпоративного кодирования могут запрещать реализацию в декларации класса даже для однострочных :) – anxieux

+1

@SteveJessop: Собственно, НЕТ! Дело в том, что вы можете поменять «Foo» по своему желанию, чтобы включить предварительное/пост-лечение перед вызовом функции «virtual». Это всегда было однострочным, было бы совершенно бессмысленно включать дополнительную обертку! –

 Смежные вопросы

  • Нет связанных вопросов^_^