В чем потенциальная опасность превращения частной функции в публичную?

Я просто узнал, что замена частной функции на публичную из базового объекта разрешена в C ++, поскольку Visual Studio выдает 0 предупреждений. Есть ли в этом потенциальная опасность?

Если нет, в чем разница между объявлением виртуальной функции private, protected и public в базовом объекте?


person Rahn    schedule 21.09.2015    source источник
comment
Вы не можете переопределить метод private в Java. Я удалил упоминания об этом в вашем вопросе.   -  person Sotirios Delimanolis    schedule 21.09.2015
comment
Если кто-то использует его полиморфно, он не может вызвать частную функцию, даже если она является общедоступной в производном классе ... кстати, для меня это не имеет смысла.   -  person Melkon    schedule 21.09.2015
comment
@Rahn overriding a private function to a public one from base object is allowed in C++ since Visual Studio produces 0 warning будьте осторожны, делая подобные предположения. То, что было выдано 0 предупреждений, не означает, что это разрешено в C ++, это означает, что это разрешено в Visual Studio. И даже тогда может появиться предупреждение, если уровень предупреждения повышен (я точно не знаю). В любом случае, дело в том, что даже если один компилятор с этим справляется, это не значит, что подойдет другой. Настоящим авторитетом здесь является стандарт C ++.   -  person Steve    schedule 21.09.2015


Ответы (5)


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

Разница в том, что виртуальная функция private может быть вызвана только из базового класса. Это может быть полезно, если функция не является частью интерфейса внешнего класса и используется только базовым классом. Таким образом, пользователи вызывают член (некоторого другого) базового класса, и этот член вызывает виртуальную функцию. Например:

class Base {
    virtual void stage1()=0;  // derived classes override this
    virtual void stage2()=0;
  public:
    void run() { stage1(); stage2(); } // users call this
};

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

Дополнительные сведения см. В этой статье Херба Саттера:

... каждая [общедоступная] виртуальная функция выполняет две задачи: определяет интерфейс, потому что он общедоступен ...; и он определяет детали реализации, а именно внутренне настраиваемое поведение ... То, что общедоступная виртуальная функция по своей сути выполняет две существенно разные задачи, является признаком того, что она плохо разделяет задачи и что мы должны рассмотреть другой подход. Что, если мы хотим отделить спецификацию интерфейса от спецификации настраиваемого поведения реализации?

...

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

Однако я не могу сказать, действительно ли это широко используется ...

person Petr    schedule 21.09.2015

Есть ли в этом потенциальная опасность?

Я так не думаю, потому что вы все еще очень ограничены:

class Base
{
private:
    virtual void foo(){}
};

class Derived1 : public Base
{
public:
    virtual void foo(){ Base::foo(); }
};

class Derived2 : public Base
{
public:
    virtual void foo(){}
};

int main()
{
    Derived1 d1;
    d1.foo(); //error
    Base * d2 = new Derived2();
    d2->foo(); //error
}

Таким образом, в лучшем случае вы сможете вызвать перегруженную функцию (если она не вызывает функцию из базового класса из самого себя), но функция базового класса по-прежнему будет иметь такую ​​же видимость и будет недоступна.

person SingerOfTheFall    schedule 21.09.2015
comment
virtual void foo(){ Base::foo(); } Подождите ... Это действительно будет компилироваться? Здесь должна быть ошибка, не только в main(), поскольку Base::foo() недоступен для дочерних классов ... - person anderas; 21.09.2015
comment
Да, конечно, ошибка будет указывать как на эту строку, так и на строку main, в том-то и дело. Извините, если я недостаточно прояснил. - person SingerOfTheFall; 21.09.2015

При изменении видимости доступа путем переопределения в производном классе видимость базового класса не изменяется:

Итак, с:

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

class Derived : public Base {
public:
    void foo() override {};
};

потом

Derived d;
Base& b = d;

d.foo(); // valid
b.foo(); // invalid
person Jarod42    schedule 21.09.2015

Если нет, в чем разница между объявлением виртуальной функции private, protected и public в базовом объекте?

Это зависит от того, как вы получаете доступ к функции. Тип используемого объекта / указателя определяет, можете ли вы получить доступ к функции.

class Base
{
   public:

      virtual void foo() {}
};

class Derived : public Base
{
   private:

      virtual void foo() {}
};

int main()
{
   Derived* dptr = new Derived;
   Base* bptr = dptr;

   dptr->foo();  // Can't use it. Derived::foo is private
   bptr->foo();  // Can use it. Base::foo is public.
}

Сообщение компилятора с использованием g ++ 4.9.3.

socc.cc: In function ‘int main()’:
socc.cc:12:20: error: ‘virtual void Derived::foo()’ is private
       virtual void foo() {}
                    ^
socc.cc:20:14: error: within this context
    dptr->foo();  // Can't use it. Derived::foo is private
person R Sahu    schedule 21.09.2015

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

person Cheers and hth. - Alf    schedule 21.09.2015