Здравствуйте, ArtDenis, Вы писали:
AD>Здравствуйте, Flamer, Вы писали:
F>> Есть некий класс, в котором отсутствует конструктор без параметров. F>> Интересные места выделены жирным: [ccode]
AD>У... Как всё у нас тут запущено...
AD>Сейчас кое-кто забросает меня тухлыми яйцами, но если сделать вот так:
AD>
AD>то можно избавиться от конструктора копирования, оператора присваивания, оператора new и геммороя с освобождением памяти. И код будет в четыре раза короче.
AD>ЗЫ: сижу в резиновом плаще и жду
Дождался. Большого и тухлого.
class CTask
{
int id;
std::string message;
public:
...
Опрация копирования для векторов неэффективна. Надо использовать std::string.
Здравствуйте, MaximE, Вы писали:
ME>Шахтер wrote:
>> Опрация копирования для векторов неэффективна. Надо использовать std::string.
ME>Копирование у строк эффективние? Хотелось бы узнать подробности.
ME>-- ME>Maxim Yegorushkin
При копировании вектора создаётся копия данных. При копировании строк накручивается счётчик ссылок.
Здравствуйте, ArtDenis, Вы писали:
AD>Здравствуйте, Шахтер, Вы писали:
Ш>> Дождался. Большого и тухлого. Ш>>
Ш>> class CTask
Ш>> {
Ш>> int id;
Ш>> std::string message;
Ш>> public:
Ш>> ...
Ш>>
Ш>> Опрация копирования для векторов неэффективна. Надо использовать std::string.
AD>Не все реализации std::string основаны на подсчёте ссылок. Для обычных std::string операция копировния по скорости аналогична std::vector<char>.
Это каких, обычных? Если реализация string клонирует строку при копировании, это значит -- кривая реализация.
AD>Кроме того, для std::vector<char> мы гарантированно имеем неразрывную последовательность данных в памяти (иногда это бывает очень важно).
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, Шахтер, Вы писали:
Ш>>При копировании вектора создаётся копия данных. При копировании строк накручивается счётчик ссылок.
CS>Это ты про std::string ?
Здравствуйте, Шахтер, Вы писали:
CS>>Это ты про std::string ? Ш>Ну почему. Про любую вменяемую реализацию строк.
странные ты вещи говоришь. По твоим словам выходит так.
1 std::string a;
2 std::string b;
3 a = "aaaa";
4 b = a;
5 a = "bbbb";
Либо после выполнения пятой строки значение b так же равно "bbbb", либо при выполнении пятой строки создаётся копия строки a (copy on write), а для этого строка a должна умето получать список всех своих копий.
И то и другое мало похоже на правду
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, Шахтер, Вы писали:
CS>>>Это ты про std::string ? Ш>>Ну почему. Про любую вменяемую реализацию строк.
A>странные ты вещи говоришь. По твоим словам выходит так.
A>
A>1 std::string a;
A>2 std::string b;
A>3 a = "aaaa";
A>4 b = a;
A>5 a = "bbbb";
A>
A>Либо после выполнения пятой строки значение b так же равно "bbbb", либо при выполнении пятой строки создаётся копия строки a (copy on write), а для этого строка a должна умето получать список всех своих копий. A>И то и другое мало похоже на правду
Чего-то я (наверное по недомыслию) не нахожу неоднозначностей в твоем примере... Как вариант моей трактовки:
1 std::string a;
2 std::string b;
3 a = "aaaa"; // reference count to "aaaa" = 1 (self)
4 b = a; // reference count to "aaaa" = 2 (a and b). Reference count to "aaaa" in b instance = 1;
5 a = "bbbb"; // a now owned another string ("bbbb").
// Decrement reference count to "aaaa" (if 0 - delete "aaaa"),
// then allocate new buffer for string, copy string to buffer and set
// reference count to 1 (exclusive ownership).
// In this sample, b had continue ownership to string "aaaa" with reference count = 1.
Сорри фор инглиш — пиво было хорошее, и, как следствие закона сохранения энергии — инглиш получился фиговым
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, Шахтер, Вы писали:
CS>>>Это ты про std::string ? Ш>>Ну почему. Про любую вменяемую реализацию строк.
A>странные ты вещи говоришь. По твоим словам выходит так.
A>
A>1 std::string a;
A>2 std::string b;
A>3 a = "aaaa";
A>4 b = a;
A>5 a = "bbbb";
A>
A>Либо после выполнения пятой строки значение b так же равно "bbbb", либо при выполнении пятой строки создаётся копия строки a (copy on write), а для этого строка a должна умето получать список всех своих копий. A>И то и другое мало похоже на правду
Не буду читать сейчас лекции про строки -- у меня сегодня первый день отпуска. Просто замечание. Хорошая реализация строкового класса должна иметь дешёвую реализацию операции копирования, которая должна быть не только O(1) по сложности, а я бы сказал даже O(0), но и не должна порождать исключений. Представь себе оператор сложения строк. В нем есть возврат строки. Если ты копируешь строки клонированием, то ты автоматически удваиваешь сложность операции сложения. В ряде случаев компилятор может тут соптимизировать, но не во всех (зависит от контекста, от качества оптимизатора, от опций, от фаз Луны, наконец). Для строк операция копировани -- естественная и часто используемая. Поэтому должна быть дешёвой. А иначе такой строковый класс нафик не нужен.
Здравствуйте, Шахтер, Вы писали:
Ш>Не буду читать сейчас лекции про строки -- у меня сегодня первый день отпуска. Просто замечание.
Поздравляю. с отпуском.
Ш>Хорошая реализация строкового класса должна иметь дешёвую реализацию операции копирования, которая должна быть не только O(1) по сложности, а я бы сказал даже O(0), но и не должна порождать исключений. Представь себе оператор сложения строк. В нем есть возврат строки. Если ты копируешь строки клонированием, то ты автоматически удваиваешь сложность операции сложения. В ряде случаев компилятор может тут соптимизировать, но не во всех (зависит от контекста, от качества оптимизатора, от опций, от фаз Луны, наконец). Для строк операция копировани -- естественная и часто используемая. Поэтому должна быть дешёвой. А иначе такой строковый класс нафик не нужен.
Ээээ. Ты тёплое с мягким не путай.
Первое.
Если нет RVO и проч. оптимизаций то запись
c = a + b;
должна работать так.
1) создаёться временный объект в который записывается строка a+b
2) вызывается c.operator=(временный объект).
Ты хочешь оптимизировать за счёт пункта 2. Решение правильное, но только это не копирование, а перемещение. (не COPY, а MOVE). Для реализации этой идеи никакого подсчёта ссылок не нужно. Подробнее тут http://www.cuj.com/documents/s=8246/cujcexp2102alexandr/alexandr.htm
Второе.
Предлагая использовать std::string вместо std::vector<char> ты указывал, что копирование в std::string сделано намного эффективнее. На самом деле это не так. Во всяком случае во всех известных мне реализациях.
Максимум это то, что копирование скорее всего сделано не в цикле по элементам, а с помошью memmove. Но в любом случае никакого подсчёта ссылок там нет. Так что ты жестоко ошибался.
Здравствуйте, Flamer, Вы писали:
F>Чего-то я (наверное по недомыслию) не нахожу неоднозначностей в твоем примере... Как вариант моей трактовки:
А кто сказал, что етсь неоднозначности?
F>
F>1 std::string a;
F>2 std::string b;
F>3 a = "aaaa"; // reference count to "aaaa" = 1 (self)
F>4 b = a; // reference count to "aaaa" = 2 (a and b). Reference count to "aaaa" in b instance = 1;
F>5 a = "bbbb"; // a now owned another string ("bbbb").
F> // Decrement reference count to "aaaa" (if 0 - delete "aaaa"),
F> // then allocate new buffer for string, copy string to buffer and set
F> // reference count to 1 (exclusive ownership).
F> // In this sample, b had continue ownership to string "aaaa" with reference count = 1.
F>
Это и есть по сути copy in write. При попытке изменить исходную строку она как бы отделяется от своей копии и живёт отдельной жизнью.
Ничего невозможного в этом нет, кроме того, что это невозможно увидеть в STL Ведь мы обсуждаем не абстракный идеальный класс строки, а std::string и его преимущества перед std::vectro<char> в плане копирования.
[]
F>Чего-то я (наверное по недомыслию) не нахожу неоднозначностей в твоем примере... Как вариант моей трактовки:
F>
F>1 std::string a;
F>2 std::string b;
F>3 a = "aaaa"; // reference count to "aaaa" = 1 (self)
F>4 b = a; // reference count to "aaaa" = 2 (a and b). Reference count to "aaaa" in b instance = 1;
F>5 a = "bbbb"; // a now owned another string ("bbbb").
F> // Decrement reference count to "aaaa" (if 0 - delete "aaaa"),
F> // then allocate new buffer for string, copy string to buffer and set
F> // reference count to 1 (exclusive ownership).
F> // In this sample, b had continue ownership to string "aaaa" with reference count = 1.
F>
С copy on write не все так хорошо как кажется. Например:
string a = "aaaa";
string b = a;
cout << a[1] << '\n'; // (*)
Оператор [] для неконстантной строки должен вернуть std::allocator<char>::reference, т.е. char&. Поскольку нельзя допускать, чтобы shared buffer был изменен через эту ссылку, то в строчке (*) должна быть создана копия исходной строки.
Вот такой уродский интерфейс у std::string
F>Сорри фор инглиш — пиво было хорошее, и, как следствие закона сохранения энергии — инглиш получился фиговым
Здравствуйте, adontz, Вы писали:
A>Если нет RVO и проч. оптимизаций то запись A>c = a + b; A>должна работать так.
Кому должна? Ты указывал на известные тебе реализации, ну так как обстоит дело в них?
A>1) создаёться временный объект в который записывается строка a+b A>2) вызывается c.operator=(временный объект). A>Ты хочешь оптимизировать за счёт пункта 2. Решение правильное, но только это не копирование, а перемещение. (не COPY, а MOVE). Для реализации этой идеи никакого подсчёта ссылок не нужно. Подробнее тут http://www.cuj.com/documents/s=8246/cujcexp2102alexandr/alexandr.htm
MOJO -- трюкачество.
Всё это напоминает один анекдот. Русские сделали крутой-прекрутой вездеход. Привезли его в Японию на выставку. Японцы на него посмотрели и сказали -- даа, класный вездеход. И чего эти русские только не придумают, лишь бы дороги не ремонтировать.
A>Второе.
A>Предлагая использовать std::string вместо std::vector<char> ты указывал, что копирование в std::string сделано намного эффективнее.
Нет, я писал, что в приличной реализации должно быть более эффективной. Но даже если это не так, то ты ничего не теряешь по сравнению с вектором. И есть шанс выиграть. Так что смысл использовать для хранение строк string вместо vector всё равно есть.
A>На самом деле это не так. Во всяком случае во всех известных мне реализациях.
Я видел реализации с подсчетом ссылок. Давно правда это было.
A>Максимум это то, что копирование скорее всего сделано не в цикле по элементам, а с помошью memmove. Но в любом случае никакого подсчёта ссылок там нет. Так что ты жестоко ошибался.
Повторю ещё раз. Я НЕ пользуюсь stl. Одна из причин, что не никаких гарантий, как реализован там тот или иной класс.
Здравствуйте, folk, Вы писали:
F>Здравствуйте, Flamer, Вы писали:
F>[]
F>>Чего-то я (наверное по недомыслию) не нахожу неоднозначностей в твоем примере... Как вариант моей трактовки:
F>>
F>>1 std::string a;
F>>2 std::string b;
F>>3 a = "aaaa"; // reference count to "aaaa" = 1 (self)
F>>4 b = a; // reference count to "aaaa" = 2 (a and b). Reference count to "aaaa" in b instance = 1;
F>>5 a = "bbbb"; // a now owned another string ("bbbb").
F>> // Decrement reference count to "aaaa" (if 0 - delete "aaaa"),
F>> // then allocate new buffer for string, copy string to buffer and set
F>> // reference count to 1 (exclusive ownership).
F>> // In this sample, b had continue ownership to string "aaaa" with reference count = 1.
F>>
F>С copy on write не все так хорошо как кажется. Например: F>
F>string a = "aaaa";
F>string b = a;
F>cout << a[1] << '\n'; // (*)
F>
F>Оператор [] для неконстантной строки должен вернуть std::allocator<char>::reference, т.е. char&. Поскольку нельзя допускать, чтобы shared buffer был изменен через эту ссылку, то в строчке (*) должна быть создана копия исходной строки.
Да, но без COW копии будут создаваться всегда. Т.е. экономия всё равно есть, пусть и не 100%. А вообще -- не пользуйтесь stl!
Мне кажется, что немутирующие строки с подсчетом ссылок для приложений полезнее.
Здравствуйте, adontz, Вы писали:
F>>Вот такой уродский интерфейс у std::string
A>Что-то я не понял. Я специально поглядел свой STL. Этот ужас что, правда где-то есть?
Я о таких реализациях ничего не знаю (честно говоря работал только с Dinkumware и STLport). Говорил чисто теоретически, ведь если делать string с COW, то придется как-нибудь разруливать подобные проблемы.
Еще будет чревато передавать shared-строку в другой поток. Не помню кто, кажется Саттер или Мейерс рассматривали COW применительно к строкам.
Здравствуйте, Шахтер, Вы писали:
Ш>Да, но без COW копии будут создаваться всегда. Т.е. экономия всё равно есть, пусть и не 100%.
С COW усложнится релизация.
Ш>А вообще -- не пользуйтесь stl!
Как так? Пользоваться самодельными контейнерами/алгоритмами? Ты зовешь нас в каменный век!
Ш>Мне кажется, что немутирующие строки с подсчетом ссылок для приложений полезнее.
Это да. В дополнение к контейнеру а-ля StringBuilder.
...
So deeply rooted is the idea that copy-on-write reference counting is mandatory for strings that many developers are shocked — and sometimes go into denial — when they discover that the return on investment in this technique is often negligible and sometimes negative. The long-standing belief in this old practice is, however, younger than faith in another more fundamental software engineering principle: separation of concerns. And hey, do we have concerns.
...
...
Without atomic integer operations, copy-on-write typically incurs a significant performance penalty. Even with atomic integer operations, COW can make common String operations take nearly 50% longer -- even in single-threaded programs.
In general, copy-on-write is often a bad idea in multithread-ready code. In short, the reason is that the calling code can no longer know whether two distinct String objects actually share the same representation under the covers, and so String must incur overhead to do enough serialization that calling code can take its normal share of responsibility for thread safety. Depending on the availability of more-efficient options like atomic integer operations, the impact on performance ranges from "moderate" to "profound."
...
AD>>Не все реализации std::string основаны на подсчёте ссылок. Для обычных std::string операция копировния по скорости аналогична std::vector<char>. Ш>Это каких, обычных? Если реализация string клонирует строку при копировании, это значит -- кривая реализация.
Не спорю, свои приемущества в подсчёте ссылок есть. Особенно, если программист злоупотребляет такими констркуциями как:
void some_func(std::string arg1, std::string arg2) // так пишет "криворукий" программист :))
{
Или при сортировке строк. Правда, если переопределить функцию swap:
то это приемущество отпадает. Никаких других приемуществ я не знаю. Если я копирую одну строчку в другую, то то с вероятностью 95% я это делаю для её модификации, и подсчёт сылок мне нафиг не нужен.
AD>>Кроме того, для std::vector<char> мы гарантированно имеем неразрывную последовательность данных в памяти (иногда это бывает очень важно).
Ш>При работе со строками, обычно это неважно.
Когда как.