template<typename T>
someclass<T>& operator=(const someclass<T>& other)
{
typename std::vector<T *>::const_iterator rhs;
typename std::vector<T *>::iterator lhs;
//identity test
//this->data is std::vector<T *>
for(lhs = this->data.begin(); lhs != this->data.end(); lhs++)
{
delete *lhs;
}
this->data.clear(); // this is what I forgot
this->data.reserve(other.data.size());
for (rhs = other.data.begin(); rhs != other.data.end(); rhs++)
{
if (NULL == *rhs)
{
this->data.push_back(NULL);
}
else
{
this->data.push_back(new T(**rhs));
}
}
}
正如您在评论中看到的,我忘记清除数组中的旧指针。当我第二次调用赋值运算符时,我收到 glibc 错误,抱怨双重释放。提供的唯一信息是已删除的地址。
这让我思考如何处理此类已删除的指针 - 当您不想再次删除它们时,而当您这样做时,这肯定是一个错误。您不能将它们设置为 NULL,因为此时另一个删除将是正确的。您不想保留该值,因为内存位置可以分配给新创建的对象。
对调试有好处的是一些值,例如 INVALID,您分配给这些指针表示“在此指针上调用删除是一个错误”,而不是 NULL,它表示“在此指针上调用删除不会执行任何操作”。有这样的事吗?
template<typename T>
someclass<T>& operator=(const someclass<T>& other)
{
typename std::vector<T *>::const_iterator rhs;
typename std::vector<T *>::iterator lhs;
//identity test
//this->data is std::vector<T *>
for(lhs = this->data.begin(); lhs != this->data.end(); lhs++)
{
delete *lhs;
}
this->data.clear(); // this is what I forgot
this->data.reserve(other.data.size());
for (rhs = other.data.begin(); rhs != other.data.end(); rhs++)
{
if (NULL == *rhs)
{
this->data.push_back(NULL);
}
else
{
this->data.push_back(new T(**rhs));
}
}
}
As you can see in the comments, I forgot to clear out the old pointers in the array. When I invoked the assignment operator for the second time, I got glibc error complaining about double free. The only information provided was the deleted address.
This make me thinking about what to do with such class of deleted pointers - when you don't want to delete them again, and when you do, it is certainly an error. You cannot set them to NULL, because another delete would be correct then. You don't want to keep the value as the memory location can be assigned to newly created object.
What would be good for debugging is some value, like INVALID, which you assign to these pointers saying "invoking delete on this pointer is an error", instead of NULL, which say "invoking delete on this pointer does nothing". Is there something like this?
发布评论
评论(2)
不。当您想要拥有所有权语义时,更好的想法是不要使用原始指针。如果将
data
的类型设置为boost::ptr_vector
或std::vector>
那么你就不必手动管理指针的生命周期,问题就会消失。您的容器无法正确支持多态对象,因为您提供的赋值运算符在将容器中的对象分配给另一个容器时会对容器中的对象进行切片。更好的解决方案可能是只使用
std::vector
。仅当您不依赖指针容器的某些其他属性(例如指向元素的指针不失效,或可能更快的排序操作)时,这才适用。No. A better idea would be to not use raw pointers when you want to have ownership semantics. If you make the type of
data
beboost::ptr_vector<T>
orstd::vector<std::unique_ptr<T>>
then you will not have to manually manage the lifetimes of your pointers, and the problem will go away.Your container does not properly support polymorphic objects, as the assignment operator that you have presented will slice the objects in the container when they are assigned to another container. An even better solution may be to just have a
std::vector<T>
. This would only be appropriate if you were not counting on some other property of containers of pointers (such as the non-invalidation of pointers to elements, or potentially faster sorting operations).此问题的解决方案是编写不包含任何删除的代码。尽可能使用
shared_ptr
。当您有一个拥有多态对象的容器时,您还可以使用 指针容器。The solution to this problem is to write code that does not contain any deletes. Use
shared_ptr
where possible. When you have a container that owns polymorphic objects, you can also use Pointer Container.