Как расширить функциональность класса std::vector в C++?

Ответ

Прямое наследование от std::vector не рекомендуется, так как у него нет виртуального деструктора, что может привести к утечкам памяти. В C++ предпочтительны два подхода: композиция и свободные функции (не-члены).

1. Композиция (предпочтительно): Создайте свой класс, который содержит std::vector как приватный член и предоставляет нужный интерфейс.

template<typename T>
class MyVector {
private:
    std::vector<T> data;
public:
    // Делегирование стандартных методов
    void push_back(const T& value) { data.push_back(value); }
    size_t size() const { return data.size(); }
    T& operator[](size_t idx) { return data[idx]; }
    const T& operator[](size_t idx) const { return data[idx]; }

    // Добавление новой функциональности
    void remove_duplicates() {
        std::sort(data.begin(), data.end());
        auto last = std::unique(data.begin(), data.end());
        data.erase(last, data.end());
    }

    // Предоставление доступа к итераторам для работы с алгоритмами STL
    auto begin() { return data.begin(); }
    auto end() { return data.end(); }
};

2. Свободные функции (следуя принципам STL): Многие операции в STL реализованы как шаблонные функции, работающие с итераторами. Это наиболее гибкий и расширяемый способ.

template<typename T>
void custom_algorithm(std::vector<T>& vec) {
    // Работа с вектором через его публичный интерфейс
    if (!vec.empty()) {
        std::transform(vec.begin(), vec.end(), vec.begin(),
                       [](const T& val) { return val * 2; });
    }
}

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

Ответ 18+ 🔞

А, ну это же классика, блядь! Слушай, тут каждый второй начинающий наступает на эти грабли, как слепой кот на лего. Ебать мои старые костыли, прям ностальгия накрывает, как будто на дворе 2002-й год и я первый раз пытаюсь отнаследоваться от vector<int>.

Так вот, чувак, запомни раз и навсегда: наследоваться от std::vector — это как пытаться доебаться до тигра в клетке голыми руками. Идея вроде зрелая, а на деле — себе дороже. Почему? Да потому что у него, у суки, деструктор не виртуальный! Ну представь: ты создал свой SuperVector, навешал на него кучу своей логики, а потом через указатель на базовый vector его удаляешь. И что? А нихуя! Твой деструктор не вызовется, ресурсы не освободятся, и будет тебе утечка памяти размером с овердохуища. Сам от себя охуеешь потом, когда приложение память жрать начнет как не в себя.

Так что, ёпта, забудь про наследование, как страшный сон. Есть два адекватных пути, и оба — пизда рулю.

Первый путь — композиция. Это когда ты не лезешь в чужую семью, а просто заводишь своего чувака, который держит std::vector у себя в кармане. Типа телохранитель.

template<typename T>
class MyVector {
private:
    std::vector<T> data; // Вот он, наш кореш, спрятан внутри
public:
    // Просто говоришь ему что делать — делегирование, называется
    void push_back(const T& value) { data.push_back(value); }
    size_t size() const { return data.size(); }
    T& operator[](size_t idx) { return data[idx]; }
    const T& operator[](size_t idx) const { return data[idx]; }

    // А тут уже твоя фантазия: хочешь — дубликаты удаляй
    void remove_duplicates() {
        std::sort(data.begin(), data.end());
        auto last = std::unique(data.begin(), data.end());
        data.erase(last, data.end());
    }

    // Итераторы дай, чтобы с STL-алгоритмами дружил
    auto begin() { return data.begin(); }
    auto end() { return data.end(); }
};

Вот это — хитрая жопа. Безопасно, надёжно, и никто тебе не запрещает свою логику пихать. Доверия к такому решению — ебать, на высоте.

Второй путь — свободные функции. Это вообще философия STL, чувак. Ты не лезешь в классы, ты работаешь с тем, что есть, через итераторы. Как настоящий джедай.

template<typename T>
void custom_algorithm(std::vector<T>& vec) {
    // Взял вектор, сделал с ним что хотел через его же методы
    if (!vec.empty()) {
        std::transform(vec.begin(), vec.end(), vec.begin(),
                       [](const T& val) { return val * 2; });
    }
}

Никаких тебе проблем с наследованием, полиморфизмом. Всё прозрачно, как слёзы ребёнка. Хочешь новую функцию — написал, и не надо ни от кого наследоваться.

А теперь главное: наследование от std::vector — это мартышлюшка. Да, технически оно может сработать, если ты на 100% уверен, что будешь работать с объектом напрямую и через указатель на базовый класс его никогда не удалять. Но это как ходить по охуенно тонкому льду — один неверный шаг, и ты уже в ледяной воде с утечкой памяти. Зачем тебе этот геморрой? Выбери нормальный путь, не будь распиздяем.