拥有一个带有非虚拟析构函数的公共基类是安全的,但是如果有人使用 分配您的类的实例,new使用 a 引用它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基类无法调用 in 中的重载myvec,因此我更改的函数根本不适用——这对您来说是非虚拟函数。我不能只是重载resize()并myvec完成它,我必须重载每个改变大小的函数并使它们全部调用check(直接或通过相互调用)。
你可以从标准中的限制推断出有些事情是不可能的:例如,operator[]不能改变向量的大小,所以在我的例子中我可以安全地使用基类实现,我只需要重载函数这可能会改变大小。但是该标准不一定会为所有可能的派生类提供这种保证。
简而言之,std::vector它并非设计为基类,因此它可能不是一个表现良好的基类。
当然,如果您使用私有继承,则无法传递myvec给需要向量的函数。但那是因为它不是向量——它的push_back函数甚至没有与向量相同的语义,所以我们在 LSP 的基础上存在问题,但更重要的是,对函数的非虚拟调用vector忽略了我们的重载。如果你按照标准库的预期做事,那就没问题了——使用大量模板,并传递迭代器而不是集合。如果你想要虚函数调用是不行的,因为除了vector没有虚析构函数之外,它没有任何虚函数。
如果你真的想要标准容器的动态多态性(也就是说,你想做
vector<int> *ptr = new myvec(1);),那么你就进入了“你不应该”的领域。标准库不能真正帮助你。