假设我有以下内容:
int main() {
int* test = new int;
*test = 5;
int* test2 = test;
}
然后,在某个地方,在某个功能中,我为test2
释放内存并将其设置为NULL
。有没有办法在同一个函数中将test
设置为NULL
而不将其传递给函数?
编辑:std::shared_ptr
无法使用
答案 0 :(得分:1)
默认情况下,当您将指针传递给函数时,您将传递值的副本:
void f(int* p) {
// p has the same value as x below, but is independent
delete p;
p = nullptr;
// p is null, but back in main 'x' still has the original value
}
int main() {
int* x = new int;
f(x);
// 'x' is unmodified and now points to a deleted block of memory
}
您的选择是通过引用传递指针或将指针传递给指针:
#include <iostream>
void by_pointer(int** p) {
delete *p;
*p = nullptr;
}
void by_reference(int*& p) {
delete p;
p = nullptr;
}
int main() {
int* x = new int;
by_pointer(&x);
std::cout << (void*)x << "\n"; // outputs null
int* y = new int;
by_reference(y);
std::cout << (void*)y << "\n"; // outputs null
}
答案 1 :(得分:1)
将指针传递给引用,因为如果你使用了普通的指针,你只能更改指向的值,并且由于两个指针都指向同一个东西,所以不需要调用{两者都是{1}}:
change()
顺便说一句,我推荐std::shared_ptr
用于这样的目的,或std::unique_ptr
修改的
上面唯一的问题是#include <iostream>
void change(int*& p)
{
delete p;
p = nullptr;
}
int main()
{
int* test = new int;
*test = 5;
int* test2 = test;
std::cout << *test; //5
std::cout << *test2; //5
change(test);
}
被删除,而不是指向test2
,但除非使用智能指针或不同的功能,否则无法更改。
答案 2 :(得分:1)
shared_ptr
和weak_ptr
类完全符合您的要求。由于您无法使用它们,因此您最好的选择是重新实现它们所需的部分。我假设您不需要任何线程安全性,并且您不必关心优化的简单性。如果您这样做,请使用标准库。
您需要一个控制对象。它应该有一个指向真实对象和两个整数的指针,一个是强指针的数量,另一个是弱指针的数量。强指针和弱指针应该有一个指向控制对象的指针。
当强指针被破坏时,减少强指针计数。如果强指针计数为零,则删除该对象并将其指针设置为NULL。如果弱指针计数也为零,则丢弃控制对象。
当弱指针被破坏时,减少弱指针计数。如果两个指针计数均为零,则丢弃控制对象。
复制指针时,必须突破计数。当弱指针被提升为强指针时,碰撞强指针计数并且如果之前为零则操作失败。
这应该足以让你有这个想法。
答案 3 :(得分:1)
如果真的想要这个(虽然我强烈建议您重新考虑您的设计),那么以下内容可能适合您:
我们将指针包装在一个结构/类中,以便能够“挂钩”我们构造和销毁这些指针:
template<typename T>
struct pointer {
因为在释放存储值时,我们还需要修改仍然指向它的所有pointer
,我们需要以某种方式跟踪它们。我会说将它们与价值一起存储:
struct boxing {
T value;
std::set<pointer<T> *> references;
};
boxing * box;
接下来是构建一个指针。我在这里简化了。您可能希望添加完美转发,构建“空指针”的可能性,等等......
pointer(T value) : box(new boxing{value}) {
add_myself();
}
如您所见,我们“添加自己”(到参考集)。当pointer
被破坏时,我们需要再次从该集合中删除自己:
~pointer() {
remove_myself();
}
在构建副本时,我们只使用原作中的box
并添加自己:
pointer(pointer const & p) : box(p.box) {
add_myself();
}
当复制分配给我们时,我们首先需要从当前box
中删除自己,使用原文的框并添加自己:
pointer & operator=(pointer const & p) {
remove_myself();
box = p.box;
add_myself();
}
我很懒。自己实施移动建设/任务;)
pointer(pointer &&) = delete;
pointer & operator=(pointer &&) = delete;
我们希望能够使用pointer
,因此我们将转换运算符添加到原始指针:
operator T*(void) {
return box ? &(box->value) : nullptr;
}
最后,释放一个指针。我们将引用集中当前box
的所有pointers
成员设置为nullptr
(这包括我们自己,因此附加指针b
),然后删除框:< / p>
void free() {
boxing * b = box;
for (pointer * p : b->references) {
p->box = nullptr;
}
delete b;
}
哦,最后但并非最不重要的是,添加和删除自己:
private:
void remove_myself() {
if (box == nullptr) return;
box->references.erase(this);
if (box->references.size() == 0) {
delete box;
}
}
void add_myself() {
if (box == nullptr) return;
box->references.insert(this);
}
};
一些功能。请注意,我传递值以强制执行另一个副本构造:
void foo(pointer<int> p) {
p.free();
}
两个pointers
,指向同一个盒装值:
int main(int, char **) {
pointer<int> a{21};
pointer<int> b = a;
*b = 42;
std::cout << *a << std::endl;
foo(a);
std::cout << "a is " << ((a == nullptr) ? "null" : "non-null") << std::endl;
return 0;
}
答案 4 :(得分:1)
独特拥有对象的共享控制器的想法当然是可怕的(原因将变得清晰)。
尽管如此,可以做到:
template<class T, class Deleter = std::default_delete<T>>
struct shared_unique
{
struct control_block
{
control_block(Deleter del, T* p) : del_(std::move(del)), ptr_(p), refs_(1) {}
Deleter del_;
T* ptr_;
std::size_t refs_;
void addref()
{
++refs_;
}
void release()
{
if (--refs_ == 0)
delete this;
}
~control_block() {
if (ptr_)
del_(ptr_);
}
};
control_block* ctrl_;
shared_unique(T* p = nullptr, Deleter del = Deleter()) : ctrl_(new control_block(std::move(del), p)) {}
shared_unique(shared_unique const& r) : ctrl_(r.ctrl_) { ctrl_->addref(); }
shared_unique& operator=(shared_unique const& r)
{
auto tmp = r;
swap(r);
return *this;
}
shared_unique(shared_unique&& r) : ctrl_(r.ctrl_) { r.ctrl_ = nullptr; }
shared_unique& operator=(shared_unique&& r)
{
auto tmp = std::move(r);
swap(tmp);
return *this;
}
~shared_unique()
{
ctrl_->release();
}
void swap(shared_unique& r) noexcept
{
std::swap(ctrl_, r.ctrl_);
}
void reset(T* p = nullptr)
{
std::swap(ctrl_->ptr_, p);
delete p;
}
T* get() const {
return ctrl_->ptr_;
}
};
int main()
{
shared_unique<int> su1(new int(5));
assert( su1.get() );
assert( *(su1.get()) == 5 );
shared_unique<int> su2 = su1;
assert( su2.get() );
assert( *(su2.get()) == 5 );
su1.reset();
assert( su1.get() == nullptr );
assert( su2.get() == nullptr );
}
问题是除非你提供某种“锁定”机制来保持指向对象在被访问时保持活动状态,否则不可能使这种安排成为线程安全的。
如果你想知道一个物体什么时候被摧毁,最好让它(或它的智能指针)在发生这种情况时发出信号并让感兴趣的观察者在插槽(或类似物)上进行侦听。