Изменить объект, переданный указателем на функцию

Я нуждаюсь:

  1. Создать ссылку на объект.

  2. Передайте его функции, которая вызывает конструктор класса объекта и выполняет некоторые вычисления над объектом.

  3. Передайте объект другой функции, которая также сделает с ним что-то.

Мой объект, который переходит ко второй функции, всегда пуст.

Я немного знаю разницу между памятью стека и кучи и использованием оператора new, но не могу с этим справиться. Я не понимаю, почему g не меняется после окончания func1.

#include <iostream>

class Grid
{
    public:
        int a;

        Grid()
        {
            a = 2;
        }
};

void func1(Grid * g);
void func2(Grid * g);

int
main ()
{
    Grid g;
    func1(&g);
    func2(&g);
}

void
func1 (Grid * g)
{
    g = new Grid();
    g->a = 5;
}

void
func2 (Grid * g)
{
    std::cout << g->a << std::endl;
}

Как сделать вывод в func2 5, потому что сейчас 2?

Этот g = new Grid(); изменяет временный указатель g, а не исходный объект. Вот почему значение Grid::a остается неизменным. Что если убрать это утверждение: g = new Grid();?

vahancho 29.05.2019 14:15

Спасибо! Почему этот указатель временный? и как с этим бороться?

Sergei Shumilin 29.05.2019 14:17

Он временный, как и любой другой параметр функции, независимо от того, является ли он указателем на значение.

vahancho 29.05.2019 14:18

@vahancho: слово «временный» здесь не совсем точно. Это "местное". Также указатель является значение!

Lightness Races in Orbit 29.05.2019 14:19

@vahancho Также, пожалуйста, не отвечайте в разделе комментариев

Lightness Races in Orbit 29.05.2019 14:23
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
600
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

Если вы хотите использовать указатели для «передачи по ссылке», просто сделайте это:

void
func1 (Grid * g)
{
    g->a = 5;
}

void
func2 (const Grid * g)
{
    std::cout << g->a << std::endl;
}

int
main ()
{
    Grid g;
    func1(&g);
    func2(&g);
}

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

В C++ вместо этого мы часто используем фактические ссылки, которые могут быть более понятными (хотя, возможно, не такими понятными на сайте вызова!):

void
func1 (Grid& g)
{
    g.a = 5;
}

void
func2 (const Grid& g)
{
    std::cout << g.a << std::endl;
}

int
main ()
{
    Grid g;
    func1(g);
    func2(g);
}

Что касается этого бита:

Pass it to a function that calls the constructor of the object's class

Объект уже построен. Вы можете скопировать-назначить из нового временного объекта, если вы действительно хотите «сбросить» его таким образом:

void
func1 (Grid& g)
{
    g = Grid();
    g.a = 5;
}

Но я бы посоветовал пересмотреть, действительно ли вы хотите/нужно сбрасывать объект таким образом. И, если вы это сделаете, может быть, хорошая функция-член reset()?

Вы не компилируете

Hatted Rooster 29.05.2019 14:21

@SombreroChicken Твоя мама тоже

Lightness Races in Orbit 29.05.2019 14:21

Но ее домашнее печенье очень вкусное.

Hatted Rooster 29.05.2019 14:25

@SombreroChicken Biscuits!

Lightness Races in Orbit 29.05.2019 14:26

Такая же разница. Цвет цветвкус вкус плавать плавать плавать мама мама ПОМОГИТЕ

Hatted Rooster 29.05.2019 14:29

Хорошо, поэтому я хочу извиниться, это произошло не из-за вашего кода, но я думаю, что микрочип моего рабочего стола неисправен.

Hatted Rooster 29.05.2019 14:38

@SombreroChicken Classic ?

Lightness Races in Orbit 29.05.2019 14:40

Но что, если я получу результаты для конструктора только внутри func1? То есть я могу избежать создания объекта внутри func1?

Sergei Shumilin 29.05.2019 14:44

@SergeiShumilin Вы можете дать своему классу функцию-член reset().

Lightness Races in Orbit 29.05.2019 14:56

На самом деле, когда вы используете new, вы меняете значение указателя.

Но в любом случае, я думаю, вы можете сделать это намного проще. Например:

void func(Grid & g)
{
    g.a = 5;
}

Обновлено: Конечно, вы должны вызывать func() так:

Grid gr;
func(gr);

Я надеюсь, что это может помочь;

Вы имели в виду инициализировать объект здесь? Нравится Grid{}?

Guillaume Racicot 29.05.2019 14:22

Но что, если я получу результаты для конструктора только внутри func1? То есть я могу избежать создания объекта внутри func1?

Sergei Shumilin 29.05.2019 14:44

@SergeiShumilin Когда вы объявляете Grid g;, вы вызываете конструктор по умолчанию. Так что вам не нужно вызывать конструктор внутри func1(). Но если вы хотите сбросить его, вы можете либо вызвать g = Grid(); внутри func1(), либо создать функцию-член reset(), как вам посоветовал @LightnessRacesInOrbit.

Fareanor 29.05.2019 15:00

Чтобы помочь вам представить это, я напишу это так:

#include <iostream>

class Grid
{
    public:
        int a;

        Grid()
        {
            a = 2;
        }
};

void func1(uint64_t g);
void func2(uint64_t g);

int
main ()
{
    Grid g;
    func1((uint64_t)&g);
    func2((uint64_t)&g);
}

void
func1 (uint64_t g)
{
    g = (uint64_t)new Grid();
    ((Grid*)g)->a = 5;
}

void
func2 (uint64_t g)
{
    std::cout << ((Grid*)g)->a << std::endl;
}

Приведенный выше код будет работать точно так же, как и другой код (в 64-битной системе). Очевидно, что это ужасно, и приведенный выше код никогда не следует использовать, но написание его таким образом может помочь вам понять указатели.

Я не думаю, что приведение всего к uint64_t — хороший способ понять указатели.

Lightness Races in Orbit 29.05.2019 14:19

Я не знаю, это демонстрирует, что указатели - это просто числа, как и любая другая переменная.

Sabrina Jewson 29.05.2019 14:20

Но это не так.

Lightness Races in Orbit 29.05.2019 14:21

Да, указатель - это число. Так получилось, что это число является адресом памяти другой переменной, но по сути это все еще число.

Sabrina Jewson 29.05.2019 14:23

Нет, это в основном указатель. То, что они могут быть представлены в виде чисел, является либо деталью реализации, либо утечкой абстракции, в зависимости от того, как вы на это смотрите. Но указатели гораздо более ограничены, чем числа. (Правда, по какой-то причине это малоизвестно.) Указатели — это не числа; они указатели.

Lightness Races in Orbit 29.05.2019 14:24

вот еще одна возможность использования указателей

Grid *  func1();
void func2(Grid * g);


int main ()
{

    Grid * g = func1();
    func2(g);
}


Grid * func1 ()
{
    Grid * g = new Grid();
    g->a = 5;
    return g;
}


void func2 (Grid * g)
{
    std::cout << g->a << std::endl;
}

Объясните свое решение. Также у вас есть утечка памяти.

Lightness Races in Orbit 29.05.2019 14:47

нужно просто удалить указатель в конце программы конечно, и добавить некоторую защиту при выделении памяти. но версия, использующая ссылку, по-прежнему лучшее решение, я думаю.

Saif Faidi 29.05.2019 14:53

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