MySQL Java JavaScript PHP Python HTML-CSS C-sharp C++ Go

Funcții virtuale pure și clase abstracte

Uneori este necesar să definim o clasă care nu presupune crearea de obiecte concrete. De exemplu, o clasă pentru o figură geometrică. În realitate, există figuri concrete: pătrat, dreptunghi, triunghi, cerc și așa mai departe. Însă o figură abstractă în sine nu există. Totodată, putem avea nevoie să definim o clasă comună pentru toate figurile, care să conțină funcționalitate generală. Pentru a descrie astfel de concepte se folosesc clasele abstracte.

Clasele abstracte sunt clase care conțin sau moștenesc (fără a suprascrie) cel puțin o funcție virtuală pură. O clasă abstractă definește o interfață care trebuie implementată de clasele derivate.

Ce sunt funcțiile virtuale pure (pure virtual functions)? Sunt funcții care nu au o implementare. Scopul lor este de a defini doar semnătura funcționalității, lăsând implementarea în seama claselor derivate. Pentru a marca o funcție ca fiind pur virtuală, declarația sa se încheie cu = 0. De exemplu, să definim o clasă abstractă care reprezintă o figură geometrică:

class Shape
{
public:
    virtual double getSquare() const = 0;     // aria figurii
    virtual double getPerimeter() const = 0;  // perimetrul figurii
};

Clasa Shape este abstractă deoarece conține cel puțin o funcție virtuală pură. În acest caz, chiar două – pentru calculul ariei și al perimetrului. Nicio funcție nu are implementare. Ele sunt marcate drept constante, dar nu este obligatoriu. Esențial este ca orice clasă derivată din Shape trebuie să ofere o implementare pentru aceste funcții.

În același timp, nu putem crea un obiect al clasei abstracte:

Shape shape{};  // eroare

Pentru a utiliza o clasă abstractă, definim următorul program:

#include <iostream>

class Shape
{
public:
    virtual double getSquare() const = 0;
    virtual double getPerimeter() const = 0;
};

class Rectangle : public Shape
{
public:
    Rectangle(double w, double h) : width(w), height(h) { }
    double getSquare() const override
    {
        return width * height;
    }
    double getPerimeter() const override
    {
        return width * 2 + height * 2;
    }
private:
    double width;
    double height;
};

class Circle : public Shape
{
public:
    Circle(double r) : radius(r) { }
    double getSquare() const override
    {
        return radius * radius * 3.14;
    }
    double getPerimeter() const override
    {
        return 2 * 3.14 * radius;
    }
private:
    double radius;
};

int main()
{
    Rectangle rect{30, 50};
    Circle circle{30};

    std::cout << "Rectangle square: " << rect.getSquare() << std::endl;
    std::cout << "Rectangle perimeter: " << rect.getPerimeter() << std::endl;
    std::cout << "Circle square: " << circle.getSquare() << std::endl;
    std::cout << "Circle perimeter: " << circle.getPerimeter() << std::endl;
}

Aici sunt definite două clase derivate din clasa abstractă Shape – Rectangle și Circle. La crearea acestor clase, toate trebuie fie să implementeze funcțiile virtuale pure, fie să le redeclare ca fiind pure. În cel de-al doilea caz, clasele derivate rămân și ele abstracte.

În exemplul de mai sus, atât Circle, cât și Rectangle sunt clase concrete și oferă implementări complete.

Ieșirea în consolă:

Rectangle square: 1500  
Rectangle perimeter: 160  
Circle square: 2826  
Circle perimeter: 188.4

Este important de menționat că o clasă abstractă poate avea funcții normale și variabile membre, poate avea mai mulți constructori, dar nu se pot crea instanțe ale acelei clase. De exemplu:

#include <iostream>

class Shape
{
public:
    Shape(int x, int y): x{x}, y{y} { }
    virtual double getSquare() const = 0;
    virtual double getPerimeter() const = 0;
    void printCoords() const
    {
        std::cout << "X: " << x << "\tY: " << y << std::endl;
    }
private:
    int x;
    int y;
};

class Rectangle : public Shape
{
public:
    Rectangle(int x, int y, double w, double h) : Shape{x, y}, width(w), height(h) { }
    double getSquare() const override
    {
        return width * height;
    }
    double getPerimeter() const override
    {
        return width * 2 + height * 2;
    }
private:
    double width;
    double height;
};

class Circle : public Shape
{
public:
    Circle(int x, int y, double r) : Shape{x, y}, radius(r) { }
    double getSquare() const override
    {
        return radius * radius * 3.14;
    }
    double getPerimeter() const override
    {
        return 2 * 3.14 * radius;
    }
private:
    double radius;
};

int main()
{
    Rectangle rect{0, 0, 30, 50};
    rect.printCoords();     // X: 0    Y: 0

    Circle circle{10, 20, 30};
    circle.printCoords();   // X: 10   Y: 20
}

În acest caz, clasa Shape are două variabile, un constructor care le inițializează și o funcție obișnuită care le afișează. În clasele derivate trebuie apelat constructorul clasei de bază. Totuși, obiectul clasei abstracte nu poate fi creat, chiar dacă aceasta are constructor.