确保提供不会失效的参考

时间:2010-10-17 20:11:45

标签: c++ pointers reference scope

我正在创建一个TextField类,它目前存储对变量的引用,每当程序员想要时,它们都可以调用TextField.show(),并且将在屏幕上读取和显示引用的变量。 / p>

但是如果引用无效,则会导致问题。例如,如果引用的变量超出范围,我就会遇到问题。

基本上,有没有办法确保用户提供的参考不会变为无效?或者,有没有更好的方法来设置这个系统?或者如果没有,在这种情况下处理无效引用的最佳方法是什么?

2 个答案:

答案 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等其他类一起使用。)