【问题标题】:c++ STL queues, references and segmentation faultc++ STL队列、引用和分段错误
【发布时间】:2010-07-02 13:43:27
【问题描述】:

通过将 java 程序转换为 c++ 来学习 C++ 的新手。以下代码在执行时会导致分段错误 (SIGSEGV)。

//add web page reference to pages queue (STL)
void CrawlerQueue::addWebPage(WebPage & webpage) {
    pagesBuffer.push(webpage);
}

//remove and return web page reference from pages queue
WebPage & CrawlerQueue::getWebPage() {
    if (pagesBuffer.size() > 0) {
        WebPage & page = pagesBuffer.front();
        pagesBuffer.pop();
        return page;
    } else
        throw "Web pages queue is empty!";
}

//code that results in segmentation fault when called
void PageParser::extractLinks(){ 
    try {
        WebPage &  page =  crawlerqueue.getWebPage();
    }catch (const char * error) {
       return;
    }
}

对上述代码修复分段错误问题的更改被突出显示(

//return a const WebPage object instead of a WebPage reference
const WebPage CrawlerQueue::getWebPage() {          <====
    if (pagesBuffer.size() > 0) {
        WebPage page = pagesBuffer.front();         <==== 
        pagesBuffer.pop();
        return page;
    } else
        throw "Web pages queue is empty!";
}

//no segmentation fault thrown with modifications
void PageParser::extractLinks(){ 
    try {
        WebPage page =  crawlerqueue.getWebPage(); <====
    }catch (const char * error) {
       return;
    }
}

什么给了?我仍在尝试理解引用和指针

【问题讨论】:

  • 感谢所有答案和 cmets。我现在明白了。

标签: c++ stl segmentation-fault queue


【解决方案1】:
pagesBuffer.pop();

此行使您的参考无效。

请记住,标准容器使用值,而不是“引用”,因此当您使用对它的引用添加对象时,实际上您在容器中添加了对象的副本。

然后使用 pop() 销毁这个对象,使任何指向它的引用或指针无效。

也许您应该存储(共享)指针而不是对象。

【讨论】:

  • 我认为 Java 和 C++ 的最大区别之一在于理解在 Java 中一切都是引用,而在 C++ 中,除非特别声明,否则一切都是值。但是,Java 引用比 C++ 引用更类似于 C++ 指针。
  • @Craig:真正重要的部分是了解垃圾收集如何与 Java 引用一起工作,因此您不必考虑对象生命周期,以及 C++ 没有垃圾收集迫使您考虑对象的生命周期。
  • @Ken Bloom:您可能需要考虑对象的生命周期,但这并不是一个巨大的负担。 Java 指针基本上等同于 boost::shared_pointer。如果您在代码中使用它,您将获得相同的基本功能,但仍然具有确定性破坏的优势以及将 RAII 与代码一起使用的能力。
  • @Martin:我同意:对象生命周期并不是一个巨大的负担,一旦你明白了记住它就在那里并理解它。虽然来自 Java,但您必须做出调整。
【解决方案2】:

一个引用(也是一个指针)指向某处的一段数据。当您拥有返回引用的getWebPage() 版本时,该引用指向pagesBuffer 内的一段数据。当您在那之后运行pop() 时,您从队列中删除了该项目,从而删除了它的内存,但您的引用仍然指向它,所以它是一个悬空引用。

当您修改代码以按值返回时,您制作了要返回的对象的副本,因此即使在您运行 pop() 之后副本仍然存在。

(C++ 不像 Java,在 Java 中,引用可以防止对象被删除——你必须自己管理。)

【讨论】:

    【解决方案3】:

    如果要在队列中存储值,则需要更改代码:

    WebPage  CrawlerQueue::getWebPage() {
        if (pagesBuffer.size() > 0) {
            WebPage  page = pagesBuffer.front();
            pagesBuffer.pop();
            return page;
        } else
            throw "Web pages queue is empty!";
    }
    

    使用 C++ 时,您需要非常清楚地了解值、引用和指针之间的区别。您还应该意识到,在 Java 中工作的编码风格在 C++ 中工作的可能性极小——这两种语言几乎没有任何共同之处,除了一些微不足道的句法相似之处。

    另外,千万不要写这样的代码:

    void PageParser::extractLinks(){ 
        try {
            WebPage &  page =  crawlerqueue.getWebPage();
        }catch (const char * error) {
           return;
        }
    }
    

    默默地吞下异常总是一个非常糟糕的主意,因为(通常)是在非常靠近抛出位置的地方捕获。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-13
      相关资源
      最近更新 更多