Как работает конструктор перемещения строки?

Я читаю книгу о С++, пытаясь выучить язык. есть один пример, где он создает векторный класс, который работает только со строками с именем stringV. класс определяет функцию с именем reallocate, которая должна выделять новую динамическую память и перемещать свои строки в новую память в случае, если старая память заполняется.

void stringV::reallocate(){
    size_t newcap = size() ? size() * 2 : 1;
    auto newalloc = alloc.allocate(newcap); 
    auto mem = newalloc;
    auto elem = element; // element pointes to the first string object in the memory segment
    for (size_t i = 0; i < size(); i++){
        alloc.construct(newalloc, std::move(*elem));
        newalloc++;
        elem++;
    }
    free(); //destroys and deallocates the dynamic memory held by this object
    element = mem;
    first_free = newalloc; // one past the last element
    cap = element + newcap; // last part of the possibly unconstructed memory

}

эта функция использует конструктор перемещения класса строки в соответствии с книгой, крадет состояние переданного ему объекта путем замены указателей между строковыми объектами (которые, как я предполагаю, являются указателем на первый элемент, похожий на встроенный массив) вместо того, чтобы копировать каждый символ отдельно. например, давайте предположим, что у нас есть объект stringV с именем foo, в котором выделена динамическая память, достаточная для 3 строк. и мы начинаем с присвоения строк "one" , "two" и "three". теперь, когда мы пытаемся поместить четвертую строку, "four", этому контейнеру придется перераспределить память, вызвав reallocate Однако разве строковые объекты не строят свои символы в соседних местах один за другим (аналогично реализации встроенных массивов ). и когда мы используем конструктор перемещения, мы крадем только указатель на первый символ в строке. так что не будет ли это означать, что строковые элементы (символы) "one" , "two" и "three" все еще находятся в том же старом, теперь заполненном сегменте памяти. и новая выделенная память должна будет содержать только указатели на эти элементы и любые новые последующие строки, "four"... и т.д. которые помещаются в наш объект foo? и не повлияет ли это на эффективность (по меньшей мере), учитывая, что наши строки (массивы символов) больше не находятся в соседних местах?


person GamefanA    schedule 08.02.2015    source источник


Ответы (1)


Литералы вроде "one" и "two" действительно хранятся в соседней памяти, но строки копируют их при создании в свои буферы (литералы доступны только для чтения). Нет никакой гарантии, где строки будут выделять свои буферы; строки даже сами не выделяют свои буферы, а используют распределители.

Конструктор перемещения строки просто передает право собственности на буфер из старой строки в новую, он не меняет расположение буфера в памяти, поэтому после вашего reallocate все буферы будут по тем же адресам, что и до этого.

person StenSoft    schedule 08.02.2015
comment
Вы имеете в виду, что они хранятся в соседних местах, например [o][n][e][null][t][w][o][null], или как mem_reigon[o][n][e][null] diffrent_reigon [т] [ш] [о] [нуль] - person GamefanA; 08.02.2015
comment
Они находятся в одном и том же разделе двоичного файла программы и окажутся в одной и той же области памяти, поэтому они расположены близко друг к другу. Нет никакой гарантии, будут ли они [o][n][e][null][t][w][o][null] или [o][n][e][null][другой литерал][ другой литерал][t][w][o][null]. - person StenSoft; 08.02.2015
comment
так что в некотором смысле, когда я выделяю динамическую память в объекте stringV, я выделяю память для указателей на строки, а не для фактических символов - person GamefanA; 08.02.2015
comment
Вы выделяете память для экземпляров string. Эти экземпляры содержат указатели на свои буферы (где находятся символы), которые находятся где-то еще в памяти. Они также содержат другие данные, такие как экземпляр распределителя, размер и емкость буфера. - person StenSoft; 08.02.2015