2012-04-12 2 views
4

Я обсуждаю, как правильно обрабатывать контейнеры в качестве параметров.Должен ли я очищать() контейнеры, переданные в качестве параметра, или заменять новый объект?

У нас есть функция, которая принимает в качестве параметра контейнера, и хочет вернуть контейнер, заполненный только с тем, что функция помещает в него:

class bar; 

void foo(std::vector<bar> &bars) 
{ 
    //do stuff that fills bars. 
    //exceptions may be thrown. 
    //we may also legally return early 
    return; 
} 

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

Например:

void foo(std::vector<bar> &bars) 
{ 
    bars.clear(); 
    //do stuff that fills bars. 
    //exceptions may be thrown. 
    //we may also legally return early 
    return; 
} 

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

Например:

void foo(std::vector<bar> &bars) 
{ 
    std::vector<bar> localBars; 
    //do stuff that fills localBars. 
    //exceptions may be thrown. 
    //we may also legally return early 
    if (returnEarly) 
    { 
     bars.swap(localBars); 
     return; 
    } 
    //do more stuff that may change localBars 

    bars.swap(localBars); 
    return; 
} 

Первый пример представляет собой 'классический' метод; очистки ваших параметров, прежде чем что-либо делать и оттуда.

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

Есть ли какие-либо преимущества или недостатки в выборе одного метода над другим?

Обратите внимание, что для этой функции не требуется сильная гарантия исключения; если функция не сработает ничего в параметрах или что-то еще, что она имеет значение, к тому времени, когда она встанет на обработчик исключений.

+0

http://stackoverflow.com/faq: Если ваша мотивация для задавания вопроса: «Я бы хотела участвовать в обсуждении ______», тогда вы не должны спрашивать здесь. –

+0

Я в дискуссии о ..., каковы плюсы и минусы для любой стороны, кажется мне совершенно правильным вопросом – Grizzly

+1

Почему бы не вернуть результат? Тогда функция не может иметь побочных эффектов, что является наиболее надежной гарантией исключения. –

ответ

1

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

Преимущества clear:

  • повторное использование памяти
  • не старомодной информация остается в bars

Преимущества swap:

  • сильная гарантия исключением

вопрос нельзя ответить в общем , какая семантика/гарантии что вы ищете в вашем случае?

+0

Наверное, лучшее решение/резюме, которое я собираюсь получить, и лучше, чем тип ответа «мы всегда делали это так», который я получал локально, что заставило меня спросить об этом здесь, в первую очередь. В частности, в моих случаях это будет зависеть от задействованных функций. Я хотел получить лучшее представление о преимуществах каждого метода и убедиться, что в любом решении, которое я не видел, не было ни слона. Спасибо всем, кто ответил (или кто ответит). – Taeolas

1

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

Существует еще одно важное отличие: clear() не освобождает память или уменьшает емкость. Если код клиента вызывает вашу функцию в цикле, с вектором, определенным вне цикла, вектор будет быстро достигнет своего максимального размера, и у вас не будет никаких перераспределений при заполнении . С стратегией свопа, конечно, вы всегда перераспределяете. И если вас не интересует такая проблема производительности, вы должны вернуть вектор , не принимая в качестве параметра неконстантную ссылку на него .

1

своп() лучше относительно исключений. clear может иметь преимущество, если контейнер огромен, и у вас есть ограничения памяти. более функциональный/очиститель стиль может быть:

std::vector<bar> void foo() 
{ 
    std::vector<bar> bars; 
    ... 
    return bars; 
} 

РВО будет заботиться о нем большую часть времени и с C++ 11 и перемещение конструкторов/уступке это будет просто эффективным.

+0

В идеале я бы хотел вернуть бары, но примеры, с которыми я имею дело в настоящий момент, не могут обрабатывать этот рефакторинг в это время. В будущем это был бы маршрут, на который я бы предпочел войти. – Taeolas

+1

Я почти упомянул о возможности возврата ценности. Обычная причина использования интерфейса, который у него есть, - это производительность, и одна из ключевых проблем производительности заключается в том, что если вы постоянно используете один и тот же вектор в вызовах, он в ближайшее время достигнет достаточной емкости, чтобы не требовалось никаких новых распределений. Невозможно достичь этого, используя возвращаемые значения. –

+0

@ Джеймс, я полностью согласен с вами в отношении производительности. Но нужно быть осторожным при выполнении преждевременных оптимизаций. Я видел слишком много «копейки, фальшивые глупые» оптимизации, которые были сделаны при ужасном компромиссе с долгосрочной ремонтопригодностью проекта. «Это зависит» – dpiskyulev

Смежные вопросы