Можно ли скопировать указатель, а затем изменить значение, на которое он указывает, не меняя того, на что указывает исходный указатель?
Например, скажем, у меня было следующее:
int *i;
auto j = i;
*j = new_val;
Это изменит значение, хранящееся по адресу, на который указывает i
, потому что и i
, и j
указывают на один и тот же адрес памяти, чего я не хочу.
Поскольку этот пример настолько прост, я мог бы просто создать новый int *j
, не копируя i
. Но в более сложных случаях, таких как связанный список, я бы не хотел воссоздавать связанный список.
Итак, я думаю, мой вопрос сводится к тому, можно ли скопировать указатель и указать точку копирования на другой адрес, но сохранить структурную целостность исходного указателя?
Вам нужно будет каким-то образом клонировать объект. В самом языке нет ничего, что делало бы это за вас. Объекты, конечно, могут иметь конструкторы копирования, которые могут здесь помочь, но вы все равно должны написать код, чтобы использовать его для клонирования объекта. Для такой вещи, как связанный список, нетривиально, как это будет работать.
@sjsam Я никогда раньше не использовал умные указатели (то, с чем я хочу поближе познакомиться в ближайшем будущем). Я просто мельком посмотрел unique_ptr
, и да, он не позволил бы вам сделать копию i
. Но unique_ptr
не поможет мне добиться того, чего я хочу в этом случае, верно? Вы только что упомянули об этом, чтобы отметить, что я не могу сделать auto j = i
, если i
было unique_ptr
?
@SamiKuhmonen а, понятно. Итак, похоже, что мне нужно клонировать структуру, и нет простого способа скопировать ее.
Сначала небольшое уточнение. Указатель — это автономный Переменная, который содержит адрес памяти. Ни больше ни меньше. Тип указателя означает, что вы, программист, сообщили компилятору, что хранится по этому адресу, чтобы вы могли получить значение, хранящееся по этому адресу, — это называется разыменованием.
Теперь, имея это в виду, вы должны быть в состоянии решить, что вы хотите сделать самостоятельно, но давайте пройдемся по процессу, и мы будем использовать интеллектуальные указатели, потому что они помогают нам управлять памятью:
std::unique_ptr<int> i = std::make_unique<int>(10);
//Here we created a variable on the heap and gave it a value 10,
//the address of it is stored in **i**
std::unique_ptr<int> j = std::make_unique<int>(*i);
//Here we created a variable on the heap nad gave it a value
//stored at the address in **i**. Notice the dereferencing
//that means we want to copy the value pointed to,
//not the pointer itself.
Теперь без интеллектуальных указателей, если это имеет для вас смысл:
int *i = new int(10);
int *j = new int(*i);
delete i;
delete j;
Это делает то же самое, но вам нужно самостоятельно управлять памятью, и она будет просачиваться, например, при возникновении исключения.
Теперь это будет работать для всех типов, которые являются либо тривиальными, такими как int
в примере, либо теми, для которых определен конструктор копирования. Компилятор обычно генерирует один для ваших типов автоматически, если он не может этого сделать по какой-то причине, и в этом случае вам придется предоставить его самостоятельно:
Подробнее см. здесь: Копировать конструкторы
Обновлено: некоторые очень хорошие объяснения указателей, включая демонстрацию того, как делать то, что вы хотите сделать: Указатели C++ от TheCherno
Это будет работать для связанного списка? Скажем, мне дан указатель на начало связанного списка (не обязательно умный указатель и не обязательно выделенный в куче), linked_list_node *head
. Если я сделаю что-то вроде linked_list_node *copy_head = new linked_list_head(*head)
или даже *copy_head = &(*head)` , это скопирует структуру?
@David Вам нужно где-то его хранить. Как я уже упоминал, указатель — это просто адрес, он нужен, чтобы указывать на что-то. Таким образом, вы можете либо присвоить его переменной стека, как здесь linked_list_node copy_head = *head;
, либо выделить его в куче, как здесь linked_list_node *copy_head = new linked_list_node(*head);
, либо, что еще лучше, использовать интеллектуальный указатель std::unique_ptr<linked_list_node> copy_head = std::make_unique<linked_list_node>(*head);
.
Кажется, я понимаю, как бы вы сделали это в стеке? Вы не сможете использовать интеллектуальные указатели, но есть ли способ добиться того, чего я хочу, с необработанными указателями?
@ Дэвид Первое предложение linked_list_node copy_head = *head;
О, извините, я пропустил это! Спасибо. Таким образом, в случае стека copy_head
больше не является указателем, но в случае с кучей copy_head
все еще является указателем.
@ Дэвид Нет проблем. Просто обратите внимание, что если вы используете версию, которую я использовал во втором комментарии, вам также понадобится оператор присваивания копии. Он также генерируется автоматически, но если вам нужно будет предоставить его самостоятельно, у него будет такая подпись: linked_list_node &operator=(const linked_list_node &other);
Вы должны использовать
unique_ptr
, который не позволит вам сделатьauto j = i;
. Но тогда вам тоже нужен клон. Я думаю, вы могли бы создать свой собственный класс - возможно, общий класс - а затем перегрузить оператор присваивания.