Это может быть безопасно иметь открытый класс базовый с невиртуальном деструктора но поведение не определено, если кто-то выделяет экземпляр вашего класса с new
, относится к нему с vector<...>*
, а затем удаляет его с помощью этого указатель, не возвращая его к указателю на ваш класс. Поэтому пользователи вашего класса должны знать, что этого не делать. Самый верный способ остановить их - не дать им такую возможность, отсюда предупреждение компилятора.
Чтобы справиться с этой проблемой, не навязывая своим пользователям нечетные условия, лучшим советом является то, что для общедоступных базовых классов в C++ деструктор должен быть либо открытым, либо виртуальным, либо защищенным и не виртуальным (http://www.gotw.ca/publications/mill18.htm, руководство № 4). Поскольку деструктор std::vector
не является ни тем, что означает, что он не должен использоваться как общедоступный базовый класс.
Если вы хотите определить дополнительные операции над векторами, то для каких свободных функций в C++. Что так здорово в синтаксисе Member-call .
? Большая часть <algorithm>
состоит из дополнительных операций над векторными и другими контейнерами.
Если вы хотите создать, например, «вектор с максимальным ограничением размера», который предоставит весь интерфейс vector
с модифицированной семантикой, то на самом деле C++ делает это немного неудобным по сравнению с языками, где наследование и виртуальные вызовы являются нормой. Простейшее является использование частного наследования, а затем для функций членов vector
, что вы не хотите изменить, привести их в свой класс с using
:
#include <vector>
#include <iostream>
#include <stdexcept>
class myvec : private std::vector<int> {
size_t max_size;
public:
myvec(size_t m) : max_size(m) {}
// ... other constructors
void push_back(int i) {
check(size()+1);
std::vector<int>::push_back(i);
}
// ... other modified functions
using std::vector<int>::operator[];
// ... other unmodified functions
private:
void check(size_t newsize) {
if (newsize > max_size) throw std::runtime_error("limit exceeded");
}
};
int main() {
myvec m(1);
m.push_back(3);
std::cout << m[0] << "\n";
m.push_back(3); // throws an exception
}
Вы все еще должны быть осторожными, хотя. Стандарт C++ не гарантирует, какие функции vector
называть друг друга или каким образом. Там, где эти вызовы происходят, нет никакого способа для моего базового класса vector
вызвать перегрузку в myvec
, поэтому мои измененные функции просто не будут применяться - это не виртуальные функции для вас. Я не могу просто перегрузить resize()
в myvec
и сделать это, я должен перегрузить каждую функцию, которая изменяет размер и заставляет всех звонить check
(напрямую или путем вызова друг друга).
Вы можете вывести из ограничений в стандарте, что некоторые вещи невозможны: например, operator[]
не может изменить размер вектора, поэтому в моем примере я могу использовать реализацию базового класса, и я только нужно перегружать функции, которые могут изменить размер. Но стандарт не обязательно будет предоставлять гарантии такого рода для всех мыслимых производных классов.
Короче говоря, std::vector
не предназначен для базового класса, и, следовательно, он может быть не очень хорошим поведением базового класса.
Конечно, если вы используете частное наследование, вы не можете передать myvec
функции, которой требуется вектор. Но это потому, что он не вектор - его push_back
функция даже не имеет одинаковую семантику, как вектор, поэтому мы находимся на изворотливых основаниях с LSP, но, что более важно, не виртуальные вызовы функций vector
игнорировать наши перегрузки. Это нормально, если вы делаете так, как ожидали стандартные библиотеки, - используйте множество шаблонов и передавайте итераторы, а не коллекции.Это не нормально, если вам нужны виртуальные вызовы функций, потому что, помимо того, что vector
не имеет виртуального деструктора, у него нет любых виртуальных функций.
Если вы действительно хотите динамический полиморфизм со стандартными контейнерами (то есть, вы хотите сделать vector<int> *ptr = new myvec(1);
), то вы входите в территорию «не будьте». Стандартные библиотеки вам действительно не помогут.
В значительной степени «вы не получите« публично »из стандартных контейнеров библиотеки». См. [Существует ли какой-либо реальный риск для получения контейнеров C++ STL?] (Http://stackoverflow.com/questions/922248/is-there-any-real-risk-to-deriving-from-the-c-stl -контейнеры) для обсуждения этой темы (этот вопрос можно считать дубликатом этого вопроса, они по крайней мере похожи) –