删除后指向列表元素的指针
Pointer to list element after removal
我有一个存储对象的列表。
list<MyObject> l;
我还有一个方法 return 是指向其中一个对象的指针(如果存在),否则为 nullptr。
MyObject* get(int x) {
for (std::list<MyObject>::iterator it = l.begin(); it != l.end(); ++it) {
if (it->X == x) {
return &(*it);
}
}
return nullptr;
}
如果我 get() 一个指向元素的指针,并且在我使用它时,它从另一个线程中被删除,指针变得无效,并且奇怪的事情发生了:)
我想知道的是,是否有一种方法可以在 get() 中 returning 一些特殊类型的指针,这样如果我对一个元素调用擦除并且该元素仍然被引用,在指向它的指针超出范围之前,它的内存不会被释放。
我考虑过使用引用,但我需要在获取时 returning nullptr 的可能性,所以我可以从调用者那里检查 return 是否确实是一个有效对象。
有人可以建议更好的方法来避免这些内存问题吗?
根据建议,您应该使用一些 smart_pointer 来管理共享所有权。
一些建议:
- 始终使用默认值,std::vector
- 如果可以使用 C++11,则使用标准 shared_ptr 共享所有权,如果不能,请使用 boost 版本。
- 使用algorithm header as much as you can (in this case
find_if
是正确的)。
您还应该尝试使用该算法来搜索特定元素。这是一些示例代码:
#include <algorithm>
#include <iostream>
#include <vector>
#include <memory>
struct MyObject {
int X;
MyObject(int x_value) : X(x_value) {}
};
using element_t = std::shared_ptr<MyObject>;
std::vector<element_t> l{
std::make_shared<MyObject>(3), std::make_shared<MyObject>(4),
std::make_shared<MyObject>(5), std::make_shared<MyObject>(6),
std::make_shared<MyObject>(7), std::make_shared<MyObject>(8)};
element_t get(int x) {
auto it = std::find_if(std::begin(l), std::end(l),
[x](const element_t& elem) { return elem->X == x; });
element_t found;
if (it != std::end(l)) {
found = *it;
}
return found;
}
int main() {
auto f1 = get(6);
if (f1) {
std::cout << "encontrado " << f1->X << std::endl;
} else {
std::cout << "6 no se encontro" << std::endl;
}
auto f2 = get(10);
if (f2) {
std::cout << "encontrado " << f2->X << std::endl;
} else {
std::cout << "10 no se encontro" << std::endl;
}
return 0;
}
在使用智能指针之前,您可能需要确保能够清楚地说明为什么不能(或不想)设计一个对象在给定时间只有一个所有者的系统。
智能指针会避免无效的数据访问,但它们或多或少都有各种隐藏的问题
- 它们会消耗额外的内存,迫使你在任何地方都使用它们和它们的移动语义,并且可能很容易变得棘手,例如如果您保留循环引用或想要一个对象 return 一个指向自身的智能指针,
std::
容器基本上变得毫无用处,就像你用任何类型的指针填充它们一样(指针的向量不是对象的向量),
- 您无法控制释放发生的位置,因此您的对象可能会被任何引用它们的任务删除,可能是时间紧迫的任务,
- 不清楚谁拥有什么往往是灾难的根源。
例如,让一个线程决定删除对象而另一个线程在没有任何同步的情况下从同一存储中抓取一些对象确实是非常危险的。这有点像一个线程认为该对象无效而另一个线程认为它有效。
不是最稳健的设计让我印象深刻,但你肯定有你的理由。
我认为您可以从使用 unique_ptr
s 开始,看看它是否适合您的需求,而不是立即跳到 shared_ptr
s。
我有一个存储对象的列表。
list<MyObject> l;
我还有一个方法 return 是指向其中一个对象的指针(如果存在),否则为 nullptr。
MyObject* get(int x) {
for (std::list<MyObject>::iterator it = l.begin(); it != l.end(); ++it) {
if (it->X == x) {
return &(*it);
}
}
return nullptr;
}
如果我 get() 一个指向元素的指针,并且在我使用它时,它从另一个线程中被删除,指针变得无效,并且奇怪的事情发生了:)
我想知道的是,是否有一种方法可以在 get() 中 returning 一些特殊类型的指针,这样如果我对一个元素调用擦除并且该元素仍然被引用,在指向它的指针超出范围之前,它的内存不会被释放。
我考虑过使用引用,但我需要在获取时 returning nullptr 的可能性,所以我可以从调用者那里检查 return 是否确实是一个有效对象。
有人可以建议更好的方法来避免这些内存问题吗?
根据建议,您应该使用一些 smart_pointer 来管理共享所有权。
一些建议:
- 始终使用默认值,std::vector
- 如果可以使用 C++11,则使用标准 shared_ptr 共享所有权,如果不能,请使用 boost 版本。
- 使用algorithm header as much as you can (in this case
find_if
是正确的)。
您还应该尝试使用该算法来搜索特定元素。这是一些示例代码:
#include <algorithm>
#include <iostream>
#include <vector>
#include <memory>
struct MyObject {
int X;
MyObject(int x_value) : X(x_value) {}
};
using element_t = std::shared_ptr<MyObject>;
std::vector<element_t> l{
std::make_shared<MyObject>(3), std::make_shared<MyObject>(4),
std::make_shared<MyObject>(5), std::make_shared<MyObject>(6),
std::make_shared<MyObject>(7), std::make_shared<MyObject>(8)};
element_t get(int x) {
auto it = std::find_if(std::begin(l), std::end(l),
[x](const element_t& elem) { return elem->X == x; });
element_t found;
if (it != std::end(l)) {
found = *it;
}
return found;
}
int main() {
auto f1 = get(6);
if (f1) {
std::cout << "encontrado " << f1->X << std::endl;
} else {
std::cout << "6 no se encontro" << std::endl;
}
auto f2 = get(10);
if (f2) {
std::cout << "encontrado " << f2->X << std::endl;
} else {
std::cout << "10 no se encontro" << std::endl;
}
return 0;
}
在使用智能指针之前,您可能需要确保能够清楚地说明为什么不能(或不想)设计一个对象在给定时间只有一个所有者的系统。
智能指针会避免无效的数据访问,但它们或多或少都有各种隐藏的问题
- 它们会消耗额外的内存,迫使你在任何地方都使用它们和它们的移动语义,并且可能很容易变得棘手,例如如果您保留循环引用或想要一个对象 return 一个指向自身的智能指针,
std::
容器基本上变得毫无用处,就像你用任何类型的指针填充它们一样(指针的向量不是对象的向量),- 您无法控制释放发生的位置,因此您的对象可能会被任何引用它们的任务删除,可能是时间紧迫的任务,
- 不清楚谁拥有什么往往是灾难的根源。
例如,让一个线程决定删除对象而另一个线程在没有任何同步的情况下从同一存储中抓取一些对象确实是非常危险的。这有点像一个线程认为该对象无效而另一个线程认为它有效。
不是最稳健的设计让我印象深刻,但你肯定有你的理由。
我认为您可以从使用 unique_ptr
s 开始,看看它是否适合您的需求,而不是立即跳到 shared_ptr
s。