Зачем нужны абстрактные классы в C++?

Я только что узнал о полиморфизме в моем классе ООП и мне трудно понять, как абстрактные базовые классы полезны.

какова цель абстрактного класса? Что дает определение абстрактного базового класса, которое не обеспечивается созданием каждой необходимой функции в каждом фактическом классе?

10 ответов


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

абстрактные классы не имеют экземпляров. Абстрактный класс должен иметь по крайней мере один отложенный метод (или функция). Для этого в C++ объявляется чистая виртуальная функция-член, но не определяется в абстрактном классе:

class MyClass {
    virtual void pureVirtualFunction() = 0;
}

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

" что предоставляет определение абстрактного базового класса, которое не предоставляется создавая каждую необходимую функцию в каждом фактическом классе?"

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

class A {
   void func1();
   virtual void func2() = 0;
}

class B : public A {
   // inherits A's func1()
   virtual void func2();   // Function defined in implementation file
}

class C : public A {
   // inherits A's func1()
   virtual void func2();   // Function defined in implementation file
}

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

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

есть вектор типа собака, которая содержит колли, Гончих, немецких овчарок и т. д пусть каждый из них лает. С вектором собак, который содержит колли, Гончих, немецких овчарок все, что вам нужно сделать, чтобы заставить их все лаять, это перебирать в петле for и вызывать кору на каждом. В противном случае вам придется иметь отдельный вектор колли, вектор биглей и т. д.

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

Я надеюсь это поможет вам понять цель: да, вам все равно придется реализовать bark в каждом подклассе, но общий абстрактный предок позволяет рассматривать любой подкласс как член базового класса и вызывать поведение, которое может быть концептуально похоже на bark, но на самом деле имеет очень разные реализации.


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

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

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


У меня есть собака. Аннотация класс собака с методом лай. Моя конкретная собака заставляет лаять. Другие собаки лают по-другому. Поэтому определение собаки абстрактным способом полезно.


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

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

пример:

class mobileinternet
{
public:
virtual enableinternet()=0;//defines as virtual so that each class can overwrite
};


class 2gplan : public mobileinternet

{
    private:

         int providelowspeedinternet(); //logic to give less speed.

    public:

         void enableinternet(int) {
                                     // implement logic
                                 }

};

//similarly

class 3gplan : public enableinternet
{
   private: high speed logic (different then both of the above)

   public: 
          /*    */
}

вот в этом примере вы можете понять.


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

http://en.wikibooks.org/wiki/C%2B%2B_Programming/Classes/Abstract_Classes


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

старый и несколько искусственный пример OO наличия базового класса Vehicle с производными классами Car, Motorcycle, ... предоставляет хороший пример здесь, скажем, вы хотите метод move() - вы можете реализовать способ, которым a Car или Motorcycle движется, но Vehicles Не двигаться в общем виде, так что Vehicle::move() должно быть чисто виртуальным и аннотация.


почему бы нам не создать каждую необходимую функцию в каждом классе ? (C++)

вы должны создать каждую необходимую функцию, помеченную как abstract в каждом производном классе.

Если вы спрашиваете, зачем создавать абстрактную функцию в абстрактном классе?

Это позволяет строгий полиморфизм времени выполнения.

Читайте также интерфейс против абстрактного класса (общий OO)


abstract class dog
{
bark();
}

// function inside another module

dogbarking(dog obj)
{
   dog.bark(); // function will call depend up on address inside the obj
}


// our class
ourclass: inherit dog
{
    bark()
    {
         //body
     }
}


main()
{
    ourclass obj;
    dogbarking(obj);
}

мы видим, что dogbarking-это функция, записанная в другом модуле. он знает только абстрактный класс собак. даже если он может вызвать функцию bark внутри ourclass. в основной функции мы создаем объект ourclass и переходим к функции dogbarking, где он получил, используя ссылочный объект абстрактного класса dog.


представьте, что у вас есть два метода отображения строки:

DisplayDialog(string s);
PrintToConsole(string s);

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

void foo(bool useDialogs) {
    if (useDialogs) {
        DisplayDialog("Hello, World!");
    } else {
        PrintToConsole("Hello, World!");
    }

    if (useDialogs) {
        DisplayDialog("The result of 2 * 3 is ");
    } else {
        PrintToConsole("The result of 2 * 3 is ");
    }

    int i = 2 * 3;
    string s = to_string(i);

    if (useDialogs) {
        DisplayDialog(s);
    } else {
        PrintToConsole(s);
    }        
}

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

абстрактные базовые классы-это способ развязка код, который использует некоторые функции из кода, который реализует эту функциональность. Он делает это, определяя общий интерфейс для всех различных способов выполнения задачи.

class AbstractStringDisplayer {
public:
    virtual display(string s) = 0;

    virtual ~AbstractStringDisplayer();
};

void foo(AbstractStringDisplayer *asd) {
    asd->display("Hello, World!");
    asd->display("The result of 2 * 3 is ");

    int i = 2 * 3;
    string s = to_string(i);

    asd->display(s);
}

int main() {
    AbstractStringDisplayer *asd = getStringDisplayerBasedOnUserPreferencesOrWhatever();

    foo(asd);
}

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