← Назад к вопросам

Что такое чисто вирутальный метод?

2.0 Middle🔥 111 комментариев
#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое чисто виртуальный метод?

Чисто виртуальный метод (Pure Virtual Method) — это метод в базовом классе, который не имеет реализации и ДОЛЖЕН быть переопределён во всех дочерних классах. В C++ обозначается как = 0 в конце объявления метода. Класс, содержащий хотя бы один чисто виртуальный метод, называется абстрактным классом и не может быть инстанцирован (создан объект этого класса).

Синтаксис и основные свойства

Объявление чисто виртуального метода:

class Shape {
public:
    // Чисто виртуальный метод
    virtual void draw() = 0;
    
    // Обычный виртуальный метод
    virtual ~Shape() {}
};

Ключевые характеристики:

  • Отсутствует реализация (только = 0)
  • Дочерний класс ОБЯЗАН переопределить метод
  • Базовый класс становится абстрактным
  • Нельзя создавать объекты абстрактного класса
  • Можно создавать указатели и ссылки на абстрактные классы

Пример: Абстрактный базовый класс

#include <iostream>
#include <memory>

// Абстрактный базовый класс
class DatabaseConnection {
public:
    virtual ~DatabaseConnection() {}
    
    // Чисто виртуальные методы
    virtual void connect(const std::string& uri) = 0;
    virtual void execute(const std::string& query) = 0;
    virtual std::string query(const std::string& sql) = 0;
    
    // Обычный виртуальный метод (может быть переопределён, но не обязательно)
    virtual void close() {
        std::cout << "Closing connection..." << std::endl;
    }
};

// Конкретная реализация для PostgreSQL
class PostgreSQLConnection : public DatabaseConnection {
pubate:
    std::string connection_string;
    
public:
    void connect(const std::string& uri) override {
        connection_string = uri;
        std::cout << "Connected to PostgreSQL: " << uri << std::endl;
    }
    
    void execute(const std::string& query) override {
        std::cout << "Executing SQL: " << query << std::endl;
    }
    
    std::string query(const std::string& sql) override {
        std::cout << "Running query: " << sql << std::endl;
        return "result_set";
    }
};

// Конкретная реализация для MySQL
class MySQLConnection : public DatabaseConnection {
private:
    std::string connection_string;
    
public:
    void connect(const std::string& uri) override {
        connection_string = uri;
        std::cout << "Connected to MySQL: " << uri << std::endl;
    }
    
    void execute(const std::string& query) override {
        std::cout << "Executing query on MySQL: " << query << std::endl;
    }
    
    std::string query(const std::string& sql) override {
        std::cout << "Running query on MySQL: " << sql << std::endl;
        return "mysql_result";
    }
};

int main() {
    // DatabaseConnection db;  // ОШИБКА! Нельзя создать объект абстрактного класса
    
    // Правильно: используем указатели/ссылки на абстрактный класс
    std::unique_ptr<DatabaseConnection> db = std::make_unique<PostgreSQLConnection>();
    
    db->connect("postgresql://localhost:5432/mydb");
    db->execute("INSERT INTO users ...");
    auto result = db->query("SELECT * FROM users");
    db->close();
    
    // Переключиться на другую реализацию просто
    db = std::make_unique<MySQLConnection>();
    db->connect("mysql://localhost:3306/mydb");
    db->execute("INSERT INTO users ...");
    
    return 0;
}

Различие между виртуальными и чисто виртуальными методами

Виртуальный метод (с реализацией):

class Animal {
public:
    // Виртуальный метод с реализацией по умолчанию
    virtual void makeSound() {
        std::cout << "Some generic sound" << std::endl;
    }
};

class Dog : public Animal {
public:
    void makeSound() override {
        std::cout << "Woof!" << std::endl;
    }
};

class Cat : public Animal {
    // Может не переопределять, будет использована базовая реализация
};

Чисто виртуальный метод (без реализации):

class Shape {
public:
    // Чисто виртуальный метод
    virtual void draw() = 0;  // Нет реализации
};

class Circle : public Shape {
public:
    void draw() override {  // ОБЯЗАН переопределить
        std::cout << "Drawing circle" << std::endl;
    }
};

// Пришлось бы переопределить draw для всех подклассов

Интерфейсы через чисто виртуальные методы

С++98 и более ранние версии (имитация интерфейса):

// В C++ нет ключевого слова interface, используем абстрактные классы
class ILogger {
public:
    virtual ~ILogger() {}
    virtual void log(const std::string& message) = 0;
    virtual void error(const std::string& message) = 0;
};

class FileLogger : public ILogger {
private:
    std::ofstream file;
    
public:
    FileLogger(const std::string& filename) {
        file.open(filename, std::ios::app);
    }
    
    void log(const std::string& message) override {
        file << "[LOG] " << message << std::endl;
    }
    
    void error(const std::string& message) override {
        file << "[ERROR] " << message << std::endl;
    }
};

class ConsoleLogger : public ILogger {
public:
    void log(const std::string& message) override {
        std::cout << "[LOG] " << message << std::endl;
    }
    
    void error(const std::string& message) override {
        std::cerr << "[ERROR] " << message << std::endl;
    }
};

Абстрактные классы и множественное наследование

class Flyable {
public:
    virtual ~Flyable() {}
    virtual void fly() = 0;
};

class Swimmable {
public:
    virtual ~Swimmable() {}
    virtual void swim() = 0;
};

// Duck реализует оба интерфейса
class Duck : public Flyable, public Swimmable {
public:
    void fly() override {
        std::cout << "Duck is flying" << std::endl;
    }
    
    void swim() override {
        std::cout << "Duck is swimming" << std::endl;
    }
};

Частичная реализация

class DatabaseConnection {
public:
    virtual ~DatabaseConnection() {}
    
    // Чисто виртуальные методы
    virtual void connect(const std::string& uri) = 0;
    virtual void execute(const std::string& query) = 0;
    
    // С реализацией по умолчанию (но всё ещё можно переопределить)
    virtual void disconnect() {
        std::cout << "Disconnecting..." << std::endl;
    }
};

// Промежуточный абстрактный класс
class AbstractSQL : public DatabaseConnection {
public:
    // Реализуем execute, но оставляем connect чисто виртуальным
    void execute(const std::string& query) override {
        if (!isConnected()) {
            throw std::runtime_error("Not connected");
        }
        performQuery(query);
    }
    
private:
    virtual bool isConnected() const = 0;
    virtual void performQuery(const std::string& query) = 0;
};

// Конкретная реализация
class PostgreSQL : public AbstractSQL {
public:
    void connect(const std::string& uri) override { /* ... */ }
    
private:
    bool isConnected() const override { /* ... */ }
    void performQuery(const std::string& query) override { /* ... */ }
};

Практическое применение в Backend

1. Паттерн Strategy:

class PaymentStrategy {
public:
    virtual ~PaymentStrategy() {}
    virtual void pay(double amount) = 0;
};

class CreditCardPayment : public PaymentStrategy {
public:
    void pay(double amount) override { /* Реализация */ }
};

class PayPalPayment : public PaymentStrategy {
public:
    void pay(double amount) override { /* Реализация */ }
};

2. Паттерн Factory:

class Logger {
public:
    virtual ~Logger() {}
    virtual void log(const std::string& msg) = 0;
};

class LoggerFactory {
public:
    static std::unique_ptr<Logger> createLogger(const std::string& type);
};

3. Плагины и расширяемость: Описание интерфейса плагина через чисто виртуальные методы позволяет легко добавлять новые функции.

Преимущества чисто виртуальных методов

  • Контрактное программирование: Базовый класс определяет контракт, который должны выполнить дочерние классы
  • Полиморфизм: Единообразная работа с разными реализациями
  • Гибкость: Легко добавлять новые реализации без изменения существующего кода
  • Type Safety: Компилятор проверяет, что все методы реализованы
  • Разделение интерфейса и реализации: Чёткое разделение ответственности

Вывод

Чисто виртуальные методы — это основной инструмент для создания абстрактных интерфейсов в C++. Они обеспечивают контрактное программирование, полиморфизм и гибкость, критически важные для масштабируемых backend-систем.