Способ обеспечить использование интерфейса в C ++ - PullRequest
5 голосов
/ 18 мая 2011

В C ++, скажем, у меня есть класс Derived, который реализует интерфейс класса BaseInterface, где BaseInterface имеет только чисто виртуальный функции и виртуальный деструктор:

class BaseInterface
{
  public:
    virtual void doSomething() = 0;
    ~BaseInterface(){}
};

class Derived : public BaseInterface
{
  public:
    Derived() {}
    ~Derived(){}

  protected:
    virtual void doSomething();

  private:
    int x;
};

Никакие классы за пределами иерархии классов Derived не должны вызывать Derived::doSomething() напрямую, т. Е. Доступ к ним должен осуществляться только полиморфно через класс BaseInterface. Чтобы обеспечить соблюдение этого правила, я сделал Derived::doSomething() защищенным. Это хорошо работает, но я ищу мнения за / против относительно этого подхода.

Спасибо!

Ken

Ответы [ 5 ]

9 голосов
/ 18 мая 2011

Я думаю, что вы ищете шаблон не-виртуального интерфейса (NVI): public не-виртуальный интерфейс, который вызывает защищенную или частную virtual реализацию:

class BaseInterface
{
  public:
    virtual ~BaseInterface(){}
    void doSomething() { doSomethingImpl(); }

protected:
    virtual void doSomethingImpl() = 0;
};

class Derived : public BaseInterface
{
  public:
    Derived() {}
    virtual ~Derived(){}

  protected:
    virtual void doSomethingImpl();

  private:
    int x;
};
3 голосов
/ 18 мая 2011

Если это часть интерфейса, почему вы не хотите, чтобы пользователи вызывали его?Обратите внимание, что, как это, они могут назвать это: static_cast<BaseInterface&>(o).doSomething() это просто неловкий способ сказать o.doSomething().Какой смысл использовать интерфейс ... если объект соответствует интерфейсу, то вы должны быть в состоянии использовать его, или я что-то упустил?

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

2 голосов
/ 18 мая 2011

То, что вы делаете, также упоминается в стандарте ISO / IEC 14882: 2003 (E) 11.6.1 и вы считаете, что вы правы. Кроме того, функция-член не является чисто виртуальной в данном примере. Должно быть, это верно и для чисто виртуальных функций, AFAIK.

Правила доступа (пункт 11) для виртуальной функции определяются ее объявлением и не зависят от правил для функции, которая впоследствии переопределяет ее.

[Example:

   class B 
   { 
     public:
     virtual int f();
   };

   class D : public B 
   { 
     private:
     int f();
   };

   void f() 
   {
     D d; 
     B* pb = &d; 
     D* pd = &d;

     pb->f(); // OK: B::f() is public, 
              // D::f() is invoked

     pd->f(); // error: D::f() is private
   }

—end example]

Доступ проверяется в точке вызова с использованием типа выражения, используемого для обозначения объект, для которого вызывается функция-член (B * в примере выше). Доступ к функции-члену в классе, в котором она была определена (D в приведенном выше примере), в общем случае неизвестен.

1 голос
/ 18 мая 2011

Ключ - остальная часть вашего кода. Принимайте BaseInterface * только в качестве аргумента для любых методов, требующих вызова doSomething (). Программист-клиент вынужден наследовать интерфейс, чтобы компилировать его код.

0 голосов
/ 18 мая 2011

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

class BaseInterface
{
  public:
    virtual void doSomething() = 0;
    ~BaseInterface(){}
};

class Derived : public BaseInterface
{
  public:
    Derived() {}
    ~Derived(){}

      virtual void doSomething(){}

  private:
    int x;
};

class SecondDerived : public Derived
{
  public:
    SecondDerived() {}
    ~SecondDerived(){}

  private:
    int x;
};

int main(int argc, char* argv[])
{
    SecondDerived derived;
    derived.doSomething(); //Derived::doSomething is called

    BaseInterface* pInt = &derived;
    pInt->doSomething(); //Derived::doSomething is called

    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...