Могу ли я изменить статус отдельных членов базового класса на частный? - PullRequest
5 голосов
/ 25 августа 2011

Я использую wxWidgets, где, если вы когда-либо использовали его, вы будете знать, что в базовом классе есть серия открытых функций. Недавно я столкнулся с ситуацией, когда я не хотел бы, чтобы метод SetText() вызывался непосредственно из производного класса. То есть производный класс унаследовал функцию SetText(), но я не хочу, чтобы эта функция была доступна клиентам. Вместо этого я предоставляю две новые функции, которые вызывают SetText(), но не раньше, чем будут выполнены некоторые дополнительные операции.

В настоящее время клиент (я!) Может забыть вызвать специальные функции и просто вызвать SetText(). Следовательно, некоторые дополнительные операции не будут выполняться. Эти операции достаточно тонкие, чтобы их можно было легко пропустить.

Итак, можно ли пометить отдельные функции как частные, чтобы клиент не мог их вызывать ИЛИ просто лишить возможности клиентов вызывать их напрямую (им придется использовать мои функции для косвенного вызова)?

Обратите внимание, что SetText() является , а не виртуальным.

РЕДАКТИРОВАТЬ: Для будущих программистов, которые сталкиваются с этим вопросом, проверьте как отмеченный ответ, так и ответ Дуга Т. .

Ответы [ 3 ]

4 голосов
/ 25 августа 2011

На самом деле есть два способа сделать это. Даг Т. дал очень хороший обзор первого - с использованием частного / защищенного наследования и композиции - поэтому я не буду вдаваться в подробности.

Проблема с использованием частного / защищенного наследования заключается в том, что оно маскирует все . Затем вы должны выборочно выставлять участников, которые вы все еще хотите быть публичными. Если вы хотите, чтобы все оставалось публичным, и пытаетесь скрыть только одну вещь, то это может стать большой головной болью. Это порождает потребность в втором способе сделать это - с ключевым словом using.

Например, в вашем случае вы можете просто объявить свой класс следующим образом:

class Child : public Parent
{
    //...
    private:
        using Parent::SetText; // overrides the access!
};

Этот только маскирует метод SetText!

Только запомните, что указатель на Child всегда можно привести к указателю на Parent, и доступ к этому методу снова становится возможным - но это тоже проблема с наследованием:

class Parent
{
public:
    void SomeMethod() { }
    void AnotherMethod() { }
};

class ChildUsing : public Parent
{
private:
    using Parent::SomeMethod;
};

class ChildPrivateInheritance : private Parent
{
};

void main()
{
    Parent *p = new Parent();
    ChildUsing *a = new ChildUsing();
    ChildPrivateInheritance *b = new ChildPrivateInheritance();
    p->SomeMethod();             // Works just fine
    a->SomeMethod();             //  !! Won't compile !!
    a->AnotherMethod();          // Works just fine
    ((Parent*)a)->SomeMethod();  // Compiles without a problem
    b->SomeMethod();             //  !! Won't compile !!
    b->AnotherMethod();          //  !! Won't compile !!
    ((Parent*)b)->SomeMethod();  // Again, compiles fine

    delete p; delete a; delete b;
}

Попытка доступа к SomeMethod в экземпляре ChildUsing приводит к (в VS2005):

error C2248: 'ChildUsing::SomeMethod' : cannot access private member declared in class 'ChildUsing'

Однако попытка получить доступ к SomeMethod или AnotherMethod в экземпляре ChildPrivateInheritance приводит к:

error C2247: 'Parent::SomeMethod' not accessible because 'ChildPrivateInheritance' uses 'private' to inherit from 'Parent'
3 голосов
/ 25 августа 2011
  1. Вы можете установить каждый общедоступный как частный, выполнив частное наследование и предоставив нужный интерфейс.

    class YourWidget : private BaseClass
    {
    };
    
    YourWidget widget;
    widget.SetText(); // this is private
    

    Это препятствует работе через вашу базукласс, хотя и разрушает отношения "есть".Следующее не будет компилироваться:

    BaseClass* ptr = new YourWidget(); //error, no conversion available 
    

    Это также влияет на каждого публичного члена BaseClass, и я не уверен, что он согласен с тем, как работает wxWidgets .

  2. Вы можете скрыть SetText, создав закрытую версию в производном классе.Это работает только при доступе через указатель производного класса и не обеспечивает никакой безопасности при работе через указатель базового класса.Базовый класс SetText будет по-прежнему вызываться, если у клиента есть базовый класс ptr.

  3. Вы можете обернуть BaseClass вместо того, чтобы извлекать из него, и открыть интерфейс, который вы бы хотелилайк.Это может или не может быть возможным в зависимости от того, как работает ваш код.Если вам нужен нормальный интерфейс, но он немного изменен, вам придется переопределить каждую функцию, на которую вы хотите перенаправить BaseClass.

  4. Вы можете YourWidget наследовать от секундыИнтерфейс, предоставляющий интерфейс, который вы хотели бы представить вашему коду, и передавать указатели на этот интерфейс вместо указателя на ваш BaseClass.то есть

     class IMyWidget
     {
     public:
         virtual void SpecialSetText() = 0;
     }
    
     class YourWidget : public BaseClass, public IMyWidget
     {
     public:
         void SpecialSetText() {/*use SetText in a special way*/}
     };
    

    Тогда в вашем коде вы можете передавать IMyWidget указатели вместо BaseClass / YourWidget указателей.Это дает определенным частям вашего кода определенный интерфейс в YourWidget и предотвращает (исключая дачу) другие части интерфейса от использования.

0 голосов
/ 25 августа 2011

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

...