C ++ STL очереди, ссылки и ошибка сегментации - PullRequest
3 голосов
/ 02 июля 2010

Обучение новичков в C ++ путем преобразования Java-программы в 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;
    }
}

Изменения в приведенном выше коде, которые исправляют проблему с ошибкой сегментации, выделены (<====): </p>

//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;
    }
}

Что дает? Я все еще пытаюсь понять ссылки и указатели

Ответы [ 3 ]

5 голосов
/ 02 июля 2010
pagesBuffer.pop();

Эта строка делает вашу ссылку недействительной.

Помните, что стандартный контейнер работает со значениями, а не с "ссылками", поэтому, когда вы добавляете объект, используя ссылку на него, фактически вы добавляете копиюобъект в контейнере.

Затем с помощью pop () вы уничтожаете этот объект, делая любую ссылку или указатель, указывающий на него, недействительным.

Возможно, вам следует хранить (общие) указатели вместо объектов.

3 голосов
/ 02 июля 2010

Ссылка (а также указатель) указывает на часть данных где-то.Когда у вас была версия getWebPage(), которая возвращала ссылку, эта ссылка указывала на фрагмент данных внутри pagesBuffer.Когда вы запустили pop() после этого, вы удалили этот элемент из очереди, тем самым удалив его память, но ваша ссылка все равно указала на него, так что это была висячая ссылка.

Когда вы изменили свой код для возвратапо значениям вы делали копии возвращаемого объекта, поэтому копии были все еще рядом, даже после того, как вы запустили pop().

(C ++ не похож на Java, где ссылка не позволяет объекту бытьудалено - вы сами должны это сделать.)

1 голос
/ 02 июля 2010

Если вы хотите сохранить значения в очереди, ваш код должен быть изменен:

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;
    }
}

Тихое глотание исключений всегда очень плохая идея, так как (обычно) ловит очень близко к месту броска.

...