C ++ Мелкое и глубокое копирование — отражает изменения в num_items вектора

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

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

Я понимаю, что происходит в первой части

В заявлении

vector<int> v2(v1);

вектор v1 передается как константный аргумент ссылки на векторную копию
конструктор, так v1 не может быть изменено, и переменная v2 затем
инициализируется в копию вектора v1, Каждое поле данных будет
скопированы, и любые изменения, внесенные позже в v2 не должно влиять v1, когда
значение в v1.the_data копируется, оба v1.the_data а также
v2.the_data будет указывать на тот же массив

Так как v1.the_data а также v2.the_data указать на тот же объект,
заявление

v1[2] = 10;

также меняется v2[2], По этой причине, v2 считается мелкой копией
из v1,

Однако я изо всех сил пытаюсь понять эту часть. Я не совсем уверен, почему v2.num_items также не изменится в мелкой копии.

Заявление

v1.push_back(20);

вставит 20 в v1[5] и изменится v1.num_items до 6, но будет
не изменить v2.num_items,

Мои нынешние мысли об этом таковы, что v1.the_data а также v2.the_data указывают на одно и то же место в памяти, поэтому они «делят» один и тот же вектор, поэтому, когда к его концу добавляется 20, оба вектора должны получить дополнительное целое число.

Я был бы очень признателен за помощь в понимании того, почему количество предметов не изменится для v2 когда v1 модифицируется.

2

Решение

Предполагая, что мы говорим о стандарте std::vector :

Когда вы копируете вектор в этом утверждении:

vector<int> v2(v1);

v2 создается путем копирования каждого элемента v1. v1 и v2 не разделяют их память.

Эта часть :

и v1.the_data, и v2.the_data будут указывать на один и тот же массив

Поскольку v1.the_data и v2.the_data указывают на один и тот же объект,

Неправильно.

Вы можете убедить себя, сравнивая базовые адреса массивов каждого из ваших векторов с data() функция-член.

РЕДАКТИРОВАТЬ :

Предполагая, что вы достаточно сумасшедший, чтобы не использовать std::vector и использовать реализацию, которая будет «делиться» своим внутренним массивом при копировании (я не буду говорить о проблемах с этим дизайном: кому принадлежит массив? кто delete[] Это ?)

Вопрос, поднятый вашим учителем, заключается в том, что когда v1 изменяется (например, добавляется элемент), v2 не знает об этом, и имеет неизменный размер.

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

Или :

1) вы реализуете какой-то шаблон наблюдателя, чтобы каждый вектор знал о какой-либо модификации (и это сложнее, чем кажется)

2) вы используете трюки для хранения длины в самом бэкэнд-массиве.

Вы могли бы столкнуться с подобными проблемами, чтобы сделать недействительными все итераторы, когда «разделяемый» массив изменяется с помощью одной из ссылок на векторы … Кошмар! Есть веские причины, по которым контейнеры STL были все разработан для управления собственной памятью, следовательно, всегда обеспечивает глубокую семантику копирования.

2

Другие решения

Ваш учебник говорит о std::vector из стандартной библиотеки? Если так, то это неправильно. vector<int> v2(v1); копировать конструкции v2 от v1, Это глубокая копия, два контейнера не разделяют хранилище и полностью разделены.

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

2

Это утверждение, похоже, предполагает конкретную реализацию
vector (что не соответствует std::vector). Предположим,
например, у нас очень наивная реализация:

template <typename T>
class Vector
{
T* myData;
int mySize;
int myCapacity;
public:
void push_back( T const& newValue )
{
if ( mySize == myCapacity ) {
//  Extend the capacity...
}
myData[mySize] = newValue;
++ mySize;
}
T& operator[]( int index )
{
return myData[index];
}
};

Если у вас нет конструктора копирования, когда вы копируете вектор,
все три переменные будут одинаковыми: оба вектора будут иметь
указатель на те же данные, того же размера и той же емкости.
Но это копии: когда вы используете []Вы модифицируете память
указал на myData, который одинаков в обоих векторах; когда
ты делаешь push_back на v1Вы обновляете размер из v1,
в его локальной копии размера.

Конечно, эта реализация наивна во многих отношениях.
Хорошая реализация чего-то вроде std::vector требует
изрядное количество мыслей, не только потому, что если требуется глубокая копия
семантика, но и по причинам исключительной безопасности (
конструктор T может бросить), и чтобы не навязывать
ненужные требования (в частности, конструктор по умолчанию).

Кроме того, если бы я пытался использовать плохо реализованный вектор в качестве примера мелкой копии, я бы не назвал это vector, так как это сразу вызывает в воображении образ std::vector, который не должен быть плохо реализован (и не в реализациях библиотеки, которые я знаю).

2

Проблема в понимании утверждения в этом вопросе заключается в том, следует ли рассматривать вектор как std :: vector или как теоретическую реализацию. std :: vector не допускает поверхностного копирования, и причина указана в утверждении: из-за этого нельзя уважать инварианты.

Теперь возьмем теоретическую реализацию с членами «the_data» и «num_items». Здесь копирование вектора должно дать глубокую копию, но простое копирование «the_data» дает поверхностную копию, потому что копируется только указатель. Это создает несколько проблем: адаптация фактических данных в одном векторе приведет к несовместимому состоянию в другом, и управление памятью больше не может быть выполнено.

0
По вопросам рекламы [email protected]