Как вы объявляете интерфейс в C ++?

Моя обычная коллекция статей управления памятью какао:

Управление памятью какао

778
задан Peter Mortensen 31 December 2009 в 08:58
поделиться

9 ответов

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

class IDemo
{
    public:
        virtual ~IDemo() {}
        virtual void OverrideMe() = 0;
};

class Parent
{
    public:
        virtual ~Parent();
};

class Child : public Parent, public IDemo
{
    public:
        virtual void OverrideMe()
        {
            //do stuff
        }
};

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

667
ответ дан Community 31 December 2009 в 08:58
поделиться

Можно также считать классы контракта реализованными с NVI (Не Виртуальный Интерфейсный Шаблон). Например:

struct Contract1 : boost::noncopyable
{
    virtual ~Contract1();
    void f(Parameters p) {
        assert(checkFPreconditions(p)&&"Contract1::f, pre-condition failure");
        // + class invariants.
        do_f(p);
        // Check post-conditions + class invariants.
    }
private:
    virtual void do_f(Parameters p) = 0;
};
...
class Concrete : public Contract1, public Contract2
{
private:
    virtual void do_f(Parameters p); // From contract 1.
    virtual void do_g(Parameters p); // From contract 2.
};
4
ответ дан Luc Hermitte 31 December 2009 в 08:58
поделиться

Немного дополнения к тому, что описано там:

Первый, удостоверьтесь, что Ваш деструктор также чист виртуальный

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

4
ответ дан Uri 31 December 2009 в 08:58
поделиться

Мой ответ является в основном тем же как другими, но я думаю, что существует две других важных вещи сделать:

  1. Объявляют виртуальный деструктор в Вашем интерфейсе или делают защищенный невиртуальный для предотвращения неопределенных поведений, если кто-то пытается удалить объект Использования типа IDemo.

  2. виртуальное наследование для предотвращения проблем со множественным наследованием. (Существует чаще множественное наследование, когда мы используем интерфейсы.)

И как другие ответы:

  • Делают класс с чистыми виртуальными методами.
  • Использование интерфейс путем создания другого класса, который переопределяет те виртуальные методы.

    class IDemo
    {
        public:
            virtual void OverrideMe() = 0;
            virtual ~IDemo() {}
    }
    

    Или

    class IDemo
    {
        public:
            virtual void OverrideMe() = 0;
        protected:
            ~IDemo() {}
    }
    

    И

    class Child : virtual public IDemo
    {
        public:
            virtual void OverrideMe()
            {
                //do stuff
            }
    }
    
32
ответ дан Jason Plank 31 December 2009 в 08:58
поделиться

Нет никакого понятия "интерфейса" по сути в C++. AFAIK, интерфейсы были сначала представлены в Java для работы вокруг отсутствия множественного наследования. Это понятие оказалось довольно полезным, и тот же эффект может быть достигнут в C++ при помощи абстрактного базового класса.

абстрактный базовый класс является классом, в котором по крайней мере одна функция членства (метод в малопонятном жаргоне Java) является чистой виртуальной функцией, объявил использование следующего синтаксиса:

class A
{
  virtual void foo() = 0;
};

абстрактный базовый класс нельзя инстанцировать, т.е. Вы не можете объявить объект класса A. Можно только получить классы из A, но любой производный класс, который не обеспечивает реализацию foo(), также будет абстрактен. Чтобы прекратить быть абстрактным, производный класс должен обеспечить реализации для всех чистых виртуальных функций, которые он наследовал.

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

И, как Mark Ransom указал, абстрактный базовый класс должен обеспечить виртуальный деструктор, точно так же, как любой базовый класс, в этом отношении.

47
ответ дан Morgoth 31 December 2009 в 08:58
поделиться

Целая причина у Вас есть специальная Интерфейсная категория типа в дополнение к абстрактным базовым классам в C# / Java, состоит в том, потому что C#/Java не поддерживают множественное наследование.

C++ поддерживает множественное наследование, и таким образом, специальный тип не необходим. Абстрактный базовый класс без некраткого обзора (чистый виртуальный) методы функционально эквивалентен интерфейсу C#/Java.

144
ответ дан Raedwald 31 December 2009 в 08:58
поделиться

Сделайте класс с чистыми виртуальными методами. Используйте интерфейс путем создания другого класса, который переопределяет те виртуальные методы.

А чистый виртуальный метод является методом класса, который определяется как виртуальный и присвоенный 0.

class IDemo
{
    public:
        virtual ~IDemo() {}
        virtual void OverrideMe() = 0;
};

class Child : public IDemo
{
    public:
        virtual void OverrideMe()
        {
            //do stuff
        }
};
235
ответ дан Warty 31 December 2009 в 08:58
поделиться

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

Запутанный?


    --- header file ----
    class foo {
    public:
      foo() {;}
      virtual ~foo() = 0;

      virtual bool overrideMe() {return false;}
    };

    ---- source ----
    foo::~foo()
    {
    }

главная причина Вы хотели бы сделать, это - то, если Вы хотите обеспечить методы интерфейса, как я имею, но делаю переопределение их дополнительным.

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

Перереализация деструктора в производном классе не является никаким грандиозным предприятием вообще - я всегда повторно реализую деструктор, виртуальный или нет, в моих производных классах.

9
ответ дан Rodyland 31 December 2009 в 08:58
поделиться

Если вы используете компилятор Microsoft C ++, тогда вы можете сделать следующее:

struct __declspec(novtable) IFoo
{
    virtual void Bar() = 0;
};

class Child : public IFoo
{
public:
    virtual void Bar() override { /* Do Something */ }
}

Мне нравится этот подход, потому что он приводит к намного меньшему размеру кода интерфейса, а размер сгенерированного кода может быть значительно меньше. Использование novtable удаляет все ссылки на указатель vtable в этом классе, поэтому вы никогда не сможете создать его экземпляр напрямую. См. Документацию здесь - novtable .

7
ответ дан 22 November 2019 в 21:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: