имеет ли смысл наследовать частным образом от абстрактного (чисто виртуального) класса?


Предположим, что эта конструкция

struct InterfaceForFoo
{
    virtual void GetItDone() = 0;
};


class APoliticallyCorrectImplementationOfFooRelatedThings : private InterfaceForFoo
{

  public:
    void GetItDone() { /*do the thing already*/ };   
};

Теперь мне интересно, есть ли у наследования приватно от интерфейса таким образом какие-либо полезные сценарии.

5 4

5 ответов:

Ха, все здесь говорят "нет". Я говорю: "Да, это имеет смысл."

class VirtualBase {
public:
    virtual void vmethod() = 0;
    // If "global" is an instance of Concrete, then you can still access
    // VirtualBase's public members, even though they're private members for Concrete
    static VirtualBase *global;
};

// This can also access all of VirtualBase's public members,
// even if an instance of Concrete is passed in,
void someComplicatedFunction(VirtualBase &obj, ...);

class Concrete : private VirtualBase {
private:
    virtual void vmethod();
public:
    void cmethod() {
        // This assignment can only be done by Concrete or friends of Concrete
        VirtualBase::global = this;
        // This can also only be done by Concrete and friends
        someComplicatedFunction(*this);
    }
};
Создание наследования private не означает, что вы не можете получить доступ к членам VirtualBase из-за пределов класса, это означает только, что вы не можете получить доступ к этим членам через ссылку на Concrete. Однако Concrete и его друзья могут приводить экземпляры Concrete к VirtualBase, и тогда любой может получить доступ к открытым членам. Просто,
Concrete *obj = new Concrete;
obj->vmethod(); // error, vmethod is private

VirtualBase *obj = VirtualBase::global;
obj->vmethod(); // OK, even if "obj" is really an instance of Concrete

В объектно-ориентированном аспекте нет случая использования такого private наследования для абстрактного class.

Однако, если вы хотите поручить, чтобы ваш ребенок class должен получить определенные методы, то вы можете использовать это. Например:

struct implement_size
{
  virtual size_t size () = 0;
};

class MyVector : private implement_size
{
public:
  size_t size () { ... } // mandatory to implement size()
}

class MyString : private implement_size
{
public:
  size_t size () { ... } // mandatory to implement size()
};
Таким образом, это просто помогает поддерживать дисциплину личного кодирования . Сообщение с этим примером заключается в том, что наследование не просто предназначено для объектно-ориентированной цели. Вы даже можете использовать наследование для остановки цепочки наследования (что-то вроде Java final).

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

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

А? Нет, это абсолютно бессмысленно, поскольку причина, по которой вы предоставляете интерфейс, заключается в том, что вы хотите, чтобы другие использовали ваш класс через этот интерфейс. Как это будет работать, если они не знают, что вы его реализуете?

#include <vector>

class Fooable{
public:
  virtual void foo() = 0;
};

class DoesFoo
  : private Fooable
{
  void foo();
};

int main(){
  std::vector<Fooable*> vf;
  vf.push_back(new DoesFoo()); // nope, doesn't work
  vf[0]->foo();
}
Приведенный выше пример не работает, потому что внешний мир не знает, что DoesFoo является Fooable, поэтому вы не можете new создать его экземпляр и назначить его Fooable*.

Не совсем. Если вам нужна функция, Вы ее реализуете. Нет смысла форсировать функцию, которая не может быть использована другими классами.

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

Если это не интерфейс, а класс, то это имеет смысл:

class A {
    virtual void foo() = 0;

    void bar() {
        foo();
    }
};

class B : private A {
    virtual void foo() {

    }
};