Может быть безопасно иметь открытый базовый класс с не виртуальным деструктором , но поведение не определено, если кто-то выделяет экземпляр вашего класса с new
, ссылается на него с vector<...>*
, а затем удаляет его, используя этот указатель, без приведения его обратно к указателю на ваш класс.Таким образом, пользователи вашего класса должны знать, чтобы не делать этого.Самый надежный способ остановить их - не дать им такую возможность, поэтому предупреждение компилятора.
Чтобы справиться с этой проблемой без необходимости навязывать такие странные условия вашим пользователям, лучший совет - для открытых базовых классов.в C ++ деструктор должен быть либо общедоступным и виртуальным, либо защищенным и не виртуальным (http://www.gotw.ca/publications/mill18.htm, рекомендация № 4).Поскольку деструктор std::vector
не является ни тем, ни другим, это означает, что его не следует использовать в качестве общедоступного базового класса.
Если все, что вам нужно, это определить некоторые дополнительные операции над векторами, то для этого нужны свободные функциив C ++.Что же такого хорошего в синтаксисе вызова участника .
?Большая часть <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);
), тогда вы входите в территорию "ты не должен".Стандартные библиотеки не могут вам помочь.