Выделение при создании копии std :: string - PullRequest
0 голосов
/ 09 марта 2011

Какая реализация, по вашему мнению, лучше?

std::string ToUpper( const std::string& source )
{
    std::string result;
    result.resize( source.length() );
    std::transform( source.begin(), source.end(), result.begin(), 
        std::ptr_fun<int, int>( std::toupper ) );
    return result;
}

и ...

std::string ToUpper( const std::string& source )
{
    std::string result( source.length(), '\0' );
    std::transform( source.begin(), source.end(), result.begin(), 
        std::ptr_fun<int, int>( std::toupper ) );
    return result;
}

Разница в том, что первая использует метод reserve после конструктора по умолчанию, новторой использует конструктор, принимающий количество символов.

EDIT 1. Я не могу использовать boost lib.2. Я просто хотел сравнить после-конструктор размещения и 1010 * после-конструктора выделения .

Ответы [ 8 ]

5 голосов
/ 09 марта 2011

а как же просто:

std::string result;
result.reserve( source.length() );

std::transform( source.begin(), source.end(), 
                std::back_inserter( result ),
                ::toupper );

Тогда лично я бы использовал буст вместо.

3 голосов
/ 09 марта 2011
#include <boost/algorithm/string.hpp>

std::string copy = boost::to_upper_copy(original);
3 голосов
/ 09 марта 2011

Первая из этих двух реализаций неверна. Функция string::reserve не меняет размер строки; скорее он просто выделяет внутреннее буферное пространство для него. Это означает, что если вы начинаете писать в строку, вы перезаписываете символы, которые являются физически частью строки, но не логически частью строки. То есть символы есть, но длина строки не будет указывать их как часть строки.

Однако я понимаю, почему вы пытаетесь использовать reserve таким образом: если вы используете resize, чтобы увеличить строку, то всем символам нужно будет присвоить какое-то значение, и это может внести некоторую неэффективность в программу. Поэтому вы ищете способ избежать этой стоимости. Если вы хотите сделать это, рассмотрите возможность использования следующего кода:

string ToUpper(const std::string& source) {
    std::string result;
    result.reserve(source.length());

    for (string::const_iterator itr = source.begin(); itr != source.end(); ++itr)
        result += std::toupper(*itr);

    return result;
}

Сначала вызывается reserve в строке результата, чтобы предварительно выделить для нее место для хранения. Затем мы просто пройдемся по элементам первой строки, преобразовав каждый из них в верхний регистр и добавив его к результирующей строке, используя operator +=. Поскольку в строке уже существует пробел из-за вызова reserve, новых выделений не должно быть, и в буфер будут записываться только те символы, которые вам нужны.

Надеюсь, это поможет!

2 голосов
/ 09 марта 2011

Второй вариант лучше, потому что он менее вероятен для записи через недифференцированный итератор Если вы только зарезервировали хранилище, вам нужно добавить символы. Строка все еще пуста. Рассмотрим std::back_insert_iterator.

1 голос
/ 09 марта 2011

Поскольку вы возвращаете преобразованную копию, почему бы не передать по значению и не преобразовать уже созданный временный объект, который вы получили?

std::string ToUpper( std::string source )
{
    std::transform( source.begin(), source.end(), source.begin(), ::toupper );
    return source;
}
1 голос
/ 09 марта 2011

Простой цикл будет самым простым:

std::string toUpper(std::string s)
{
    for(size_t i = 0, j = s.size(); i != j; ++i)
        s[i] = toupper(s[i]);
    return s;
}

Нет необходимости использовать стандартные алгоритмы и функторы здесь только для их использования.

[Update]

Вы упоминали, что не можете использовать повышение. Я просто для сравнения поставил вышеупомянутую версию, которая дважды записывает в строку результата следующую, которая записывает только один раз:

struct ToUpper
{
    typedef char result_type;
    char operator()(char c) const { return toupper(c); }
};

std::string toUpper2(std::string const& s)
{
    boost::transform_iterator<ToUpper, std::string::const_iterator> beg(s.begin());
    return std::string(beg, beg + s.size());
}

Результаты:

500000 calls
toUpper : 414nsec/call
toUpper : 414nsec/call
toUpper : 414nsec/call
toUpper2: 301nsec/call
toUpper2: 302nsec/call
toUpper2: 302nsec/call
0 голосов
/ 09 марта 2011

В первом есть ошибка копирования в пустую строку (как уже упоминалось). Другой способ исправить это - использовать back_inserter(result).

Учитывая, что первая версия выглядит несколько быстрее, поскольку она не копирует исходное значение в результат; однако возникает вопрос эффективности обычной вставки (как в 2) по сравнению со вставкой через back_inserter, т.е. push_back (как в 1).

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

0 голосов
/ 09 марта 2011

Первый лучше ... второй вводит в заблуждение, заставляя думать, что для символов установлено значение NUL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...