2010-10-17 53 views
0

我正在创建一个TextField类,该类当前存储对变量的引用,并且只要程序员需要,他们可以调用TextField.show()和引用的变量将被读取并显示在屏幕上。确保不会失效的引用

但是,如果引用变为无效,这可能会导致问题。例如,如果引用的变量超出了范围,那么我遇到了问题。

基本上,有没有什么办法可以确保用户给出的参考不会失效?或者,有没有更好的方法来建立这个系统?或者如果没有,在这种情况下处理无效引用的最佳方法是什么?

回答

5

如果您使用引用,则调用者的责任是确保他/她的代码不是未定义的。基本上,没有办法知道。您可以使用shared_ptr来确保不会发生此问题,并允许共享所有权。这意味着如果变量(shared_ptr)超出范围,则该值不会被破坏,除非没有其他人指向它自己的值。

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,则可能会很有用。如果您的TextField在WatchedPtr仍然存在的情况下被销毁,它将失败,因为当WatchedPtr尝试在析构函数中使用它时,回调指针将会失效。然而,它可以在许多例子中工作,例如,许多项目更新的状态栏文本或许多对象可能更改的进度栏中的文本。 (这是一个简化的例子,它可以在很多方面得到改进,比如对WatchedPtr进行模板化,使其适用于除字符串之外的其他类,传递函数对象或指针,以便观察者可以与TextField等其他类一起使用)