Копируя указатель, измените то, на что указывает копия, не меняя оригинал?

Можно ли скопировать указатель, а затем изменить значение, на которое он указывает, не меняя того, на что указывает исходный указатель?

Например, скажем, у меня было следующее:

int *i;
auto j = i;

*j = new_val;

Это изменит значение, хранящееся по адресу, на который указывает i, потому что и i, и j указывают на один и тот же адрес памяти, чего я не хочу.

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

Итак, я думаю, мой вопрос сводится к тому, можно ли скопировать указатель и указать точку копирования на другой адрес, но сохранить структурную целостность исходного указателя?

Вы должны использовать unique_ptr, который не позволит вам сделать auto j = i;. Но тогда вам тоже нужен клон. Я думаю, вы могли бы создать свой собственный класс - возможно, общий класс - а затем перегрузить оператор присваивания.

sjsam 07.04.2019 17:16

Вам нужно будет каким-то образом клонировать объект. В самом языке нет ничего, что делало бы это за вас. Объекты, конечно, могут иметь конструкторы копирования, которые могут здесь помочь, но вы все равно должны написать код, чтобы использовать его для клонирования объекта. Для такой вещи, как связанный список, нетривиально, как это будет работать.

Sami Kuhmonen 07.04.2019 17:17

@sjsam Я никогда раньше не использовал умные указатели (то, с чем я хочу поближе познакомиться в ближайшем будущем). Я просто мельком посмотрел unique_ptr, и да, он не позволил бы вам сделать копию i. Но unique_ptr не поможет мне добиться того, чего я хочу в этом случае, верно? Вы только что упомянули об этом, чтобы отметить, что я не могу сделать auto j = i, если i было unique_ptr?

David 07.04.2019 17:24

@SamiKuhmonen а, понятно. Итак, похоже, что мне нужно клонировать структуру, и нет простого способа скопировать ее.

David 07.04.2019 17:25
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
2 108
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Сначала небольшое уточнение. Указатель — это автономный Переменная, который содержит адрес памяти. Ни больше ни меньше. Тип указателя означает, что вы, программист, сообщили компилятору, что хранится по этому адресу, чтобы вы могли получить значение, хранящееся по этому адресу, — это называется разыменованием.

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

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 07.04.2019 17:50

@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);.

Resurrection 07.04.2019 17:55

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

David 07.04.2019 18:01

@ Дэвид Первое предложение linked_list_node copy_head = *head;

Resurrection 07.04.2019 18:02

О, извините, я пропустил это! Спасибо. Таким образом, в случае стека copy_head больше не является указателем, но в случае с кучей copy_head все еще является указателем.

David 07.04.2019 18:03

@ Дэвид Нет проблем. Просто обратите внимание, что если вы используете версию, которую я использовал во втором комментарии, вам также понадобится оператор присваивания копии. Он также генерируется автоматически, но если вам нужно будет предоставить его самостоятельно, у него будет такая подпись: linked_list_node &operator=(const linked_list_node &other);

Resurrection 07.04.2019 18:04

Другие вопросы по теме