我正在创建一个TextField类,它目前存储对变量的引用,每当程序员想要时,它们都可以调用TextField.show(),并且将在屏幕上读取和显示引用的变量。 / p>
但是如果引用无效,则会导致问题。例如,如果引用的变量超出范围,我就会遇到问题。
基本上,有没有办法确保用户提供的参考不会变为无效?或者,有没有更好的方法来设置这个系统?或者如果没有,在这种情况下处理无效引用的最佳方法是什么?
答案 0 :(得分:5)
如果您使用引用,则调用者有责任确保他/她的代码未定义。基本上,没有办法知道。您可以使用shared_ptr
确保不会发生此问题,并允许共享所有权。这意味着如果变量(shared_ptr
)超出范围,则不会破坏该值,除非没有其他人指向它自己的值。
答案 1 :(得分:1)
有参考,没有。你无法分辨,因为C ++不会自动让你知道一个引用被破坏了。
如果你切换到使用指针,你会得到一些选择。一种是使用shared_ptr
,如上所述。另一个,如果您知道TextField对象将比其中显示的对象更长,则可用的是围绕变量创建一个包装类,该类将获取指向TextField的指针并通知包装器的析构函数中的文本字段。
例如,这是一个包装类,如果std :: string超出范围,它将通知TextField。
在包装器类头文件中:
// Wraps a pointer to a string, which TextField(s) will use.
// Notify the fields using it when it goes out of scope.
class WatchedPtr
{ string * ptrS_;
std::set<TextField*> watchers_;
static void NotifyOfDeath(TextField *watcher);
WatchedPtr(const WatchedPtr&); // Prevent copying.
WatchedPtr& operator=(const WatchedPtr&); // Prevent copying.
public:
WatchedPtr(string *s) : ptrS_(s) {}
void addWatcher (TextField *watcher) { watchers_.insert(watcher); }
void removeWatcher(TextField *watcher) { watchers_.erase(watcher); }
~WatchedPtr()
{ std::for_each(watchers_.begin(), watchers_.end(), NotifyOfDeath);
delete ptrS_;
ptrS_ = NULL;
}
string* get() { return ptrS_; }
};
在包装器类cpp文件中:
void WatchedPtr::NotifyOfDeath(TextField *watcher)
{ if(watcher)
{ watcher->clientIsDead();
}
}
在TextField类头文件中:
class TextField
{
string *text_;
public:
TextField() : text_(NULL) {}
void SetTextRef(WatchedPtr &wp)
{ text_ = wp.get();
wp.addWatcher(this);
}
void show()
{ if(text_)
{ cout << "Drawing textfield with text [" << (*text_) << "]" << endl;
}
else
{ cout << "String is not valid. Drawing empty text box." << endl;
}
}
void clientIsDead() { text_ = NULL; } // do not trust the pointer anymore.
};
使用示例:
int main(int argc, char* *argv)
{
TextField tf;
{ WatchedPtr ptrString(new string("Some Text"));
tf.SetTextRef(ptrString);
tf.show(); // uses the string.
} // ptrString's destructor tells TextField to stop trusting the pointer.
tf.show(); // string is not used.
return 0;
}
虽然这有点脆弱,并不像完整的共享指针那样灵活,但如果您无法访问shared_ptr,则可能很有用。如果在WatchedPtr仍然存在的情况下销毁TextField,它将失败,因为当WatchedPtr尝试在析构函数中使用它时,回调指针将无效。然而,它可以在许多例子中起作用,例如许多项目更新的状态栏文本,或许多对象可能更改的进度栏中的文本。
(这是一个简化的例子,以获得重点。它可以通过多种方式进行改进,例如模板化WatchedPtr,使其适用于除字符串之外的其他类,传递函数对象或指针,以便观察者可以与TextField等其他类一起使用。)