Оптимизация: локальные объекты - стек или указатель на адрес в куче?

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
Здравствуйте. Пытаюсь разобраться с менеджментом памяти. Есть неясности:
C++:
//=========== Первый вариант ============================

void myF()
{
    myClass my_obj(a,b,c); // здесь объект получает адрес в стеке, выполняется конструктор
    my_obj.doSmth();
} // здесь выполнен деструктор, память стека освобождена и может быть повторно выделена

//============ Второй вариант ===========================

void myF()
{
    myClass *my_obj = new myClass(a, b, c); // здесь в стеке создается только локальный
                                            // указатель на адрес в куче, затем выполняется
                                            // конструктор. Верно?
    my_obj->doSmth();
} // здесь указатель уже удален из стека. А деструктор тоже выполнен? Память в куче
  // тоже помечена свободной? Или необходим оператор delete ?
Примеры абстрактные. При регулярном вызове второго варианта функции myF() не будет ли проблем с адресацией? Или с точки зрения системы оба варианта в равной степени оптимальны?
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
Во втором случае будут созданы два объекта: указатель+областьпамятидля класса в младших адресах кучи и указательнаобласть_памяти в старших (стеке). Второй объект удалится из памяти при восстановлении стека при выходе из функции, тогда как первый останется.
 

bort707

★★★★★★✩
21 Сен 2020
3,067
915
вторая функция ОБЯЗАТЕЛЬНО должна завершаться оператором delete
C++:
void myF()
{
    myClass *my_obj = new myClass(a, b, c); // здесь в стеке создается только локальный
                                            // указатель на адрес в куче, затем выполняется
                                            // конструктор. Верно?
    my_obj->doSmth();
    delete my_obj;
}
 
  • Лойс +1
Реакции: VVViktor

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
То есть такой вариант вообще не будет корректно работать при вызове из loop(), скажем?
C++:
uint16_t myF()
{
    uint16_t *v = new uint16_t;
    *v = my_obj.getADC();
    return *v;
}
 
Изменено:

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
А в этом варианте удаление не будет выполнено после return;
C++:
uint16_t myF()
{
    uint16_t *v = new uint16_t;
    *v = my_obj.getADC();
    return *v;
    delete v;
}
 
Изменено:

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
А здесь return *v; вернет "0";
C++:
uint16_t myF()
{
    uint16_t *v = new uint16_t;
    *v = my_obj.getADC();
    delete v;
    return *v;
}
 
Изменено:

rkit

★★★✩✩✩✩
5 Фев 2021
510
127
Во втором варианте ты выделяешь память под новый объект и не освобождаешь ее. Значит объем памяти, используемой программой, будет расти, пока память не закончится, после чего поведение программы непредсказуемо.
 

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
[Прокомментируйте, пожалуйста, этот пример. delete не сработает в функции после возврата значения?
C++:
uint16_t myF()
{
    uint16_t *v = new uint16_t;
    *v = my_obj.getADC();
    return *v;
    delete v;
}
 
Изменено:

bort707

★★★★★★✩
21 Сен 2020
3,067
915
Конечно нет, return это аналог goto, в этом месте исполнение покидает функцию и никакие строчки ниже не исполняются.
 

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
Альтернатива тогда такая получается. Глобальная v1 примет результат метода getADC, а локальная v просуществует только во время выполнения myF(). Верно?
C++:
uint16_t v1;
uint16_v *v_ptr = &v1;

uint16_t myF(*v_ptr)
{
    uint16_t *v = new uint16_t;
    v = v_ptr;
    *v = my_obj.getADC();
    delete v;
}
 
Изменено:

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
И насчет деструктора. Пример:
C++:
struct backup_me
{
    uint8_t a;
    uint32_t b;
};

class My_backup
{
    public:
      My_backup(uint8_t set_a, uint32_t set_b);
      ~My_backup();
    private:
      backup_me *v = new backup_me;
};

My_backup::My_backup(uint8_t set_a, uint32_t set_b)
{
    v->a = set_a;
    v->b = set_b;
}

My_backup::~My_backup()
{
    delete v;
}
Необходим ли такой деструктор?
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
@VVViktor, у Вас ошибки во всех примерах. Класс - ссылочный объект (адрес), (*v) имеет тип uint16_t.
 

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
Там скобки пропущены. Почему-то не могу одновременно писать сообщение и вставлять блоки кода. Устал бороться с форумом..... ((

*v = my_obj.getADC(); // метод вернет uint16_t
 

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
@poty,
Спасибо за ответы. Прошу прощения за много сообщений. Они не совсем об одном и том же. Форум сбоит. Из-за этого недопонимание. Просто не удается отредактировать вставки кода, дописать там комментарии.

Я новичок в программировании. Пишу программу, пытаюсь вникнуть в детали, хочу понимать, что делаю. Интересен механизм освобождения динамической памяти. Хочу понять в какой момент вызывается деструктор объекта, созданного в виде указателя с помощью оператора (new), если я удаляю указатель на объект в конце функции с помощью (delete). Хочу понять нужно ли специально удалять private указатели на переменные и структуры класса, созданные с помощью (new), с помощью (delete) в деструкторе.

C++:
void myF()
{
    myClass *my_obj = new myClass(a, b, c); // здесь в стеке создается только локальный
                                            // указатель на адрес в куче, затем выполняется
                                            // конструктор. Верно?
    my_obj->doSmth();
    delete my_obj; // деструктор для my_obj будет выполнен здесь?
} // или здесь? Или не будет выполнен, потому как объект уже лишится своего адреса?

//============================ На примере класса =====================================

struct backup_me
{
    uint8_t a;
    uint32_t b;
};

class My_backup
{
    public:
      My_backup(uint8_t set_a, uint32_t set_b);
      ~My_backup();
    private:
      backup_me *v = new backup_me; // после удаления объекта класса структура v не будет
};                                  // удалена из динамической памяти?

My_backup::My_backup(uint8_t set_a, uint32_t set_b)
{
    v->a = set_a;
    v->b = set_b;
}

My_backup::~My_backup() // нужно ли будет сделать это принудительно здесь?
{
    delete v;
}
 

Boroda22

★✩✩✩✩✩✩
23 Фев 2022
251
42
@VVViktor,не следует учиться языку программирования через мк, начните с простых вещей типа hello_world
 

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
@Boroda22,
Спасибо за совет. Уже начал месяц назад. Потом все стало обрастать новым кодом. Переписал все объектно-ориентированным подходом. В общем с целью обучения впихнул в программу все полученные знания, не преследуя никакой другой специальной цели. В итоге имеем это. Моя первая программа. Выкладываю только свой код. Все работает пока. И все же меня интересуют ответы по посту #19, если можно.
 

Вложения

Boroda22

★✩✩✩✩✩✩
23 Фев 2022
251
42
структура это значимый тип, располагается в стеке, и будет удален когда дойдёт очередь. объект это уже ссылочный тип, и размещается в куче. будет удален при вызове финализатора(деструктора). если не следить за удалением ненужных объектов, то произойдет утечка памяти. В .net за этим следит сборщик мусора, а здесь.... ну и в плюсах, ответственность лежит на разрабе.
 
  • Лойс +1
Реакции: VVViktor

VVViktor

✩✩✩✩✩✩✩
31 Дек 2021
39
7
@Boroda22,
Спасибо. Будет ли вызван деструктор в этом случае?
C++:
void myF()
{
    myClass *my_obj = new myClass(a, b, c); // здесь в стеке создается только локальный
                                            // указатель на адрес в куче, затем выполняется
                                            // конструктор. Верно?
    my_obj->doSmth();
    delete my_obj; // деструктор для my_obj будет выполнен здесь?
} // или здесь? Или не будет выполнен, потому как объект уже лишится своего адреса?
и будут ли удалены указатели, созданные оператором (new) в рамках класса объекта, если это специально не писать в деструкторе?
C++:
class My_backup
{
    public:
      My_backup(uint8_t set_a, uint32_t set_b);
      ~My_backup();
    private:
      backup_me *v = new backup_me; // после удаления объекта класса структура v не будет
};                                  // удалена из динамической памяти?

My_backup::My_backup(uint8_t set_a, uint32_t set_b)
{
    v->a = set_a;
    v->b = set_b;
}

My_backup::~My_backup() // нужно ли будет сделать это принудительно здесь?
{
    delete v;
}
Простите, что повторяюсь. Просто заело, что ли)) Нужны именно эти подробности. Заранее спасибо.
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
1. Почитайте про стек (stack) и про кучу (heap).
2. В первом примере строка:
myClass *my_obj = new myClass(a, b, c);
создаёт 2 объекта:
  • указатель my_obj создаётся в стеке и автоматически удаляется при выходе из области видимости;
  • класс myClass создаётся в куче и автоматически не удаляется.
Исходя из вышесказанного необходима строка:
delete my_obj;
Это именно оператор, как любой другой оператор. Для класса он вызывает деструктор.
3. Во втором примере при создании класса используется память для создания методов класса (в большинстве случаев для МК - статически, в программной памяти) затем выделяется память для создания свойств класса (может выделяться статически в области статических переменных или динамически, в куче), затем выполняется конструктор, в котором у Вас выделяется динамическая память. Динамическая память адресуется указателем, поэтому на Вас лежит обязанность его сохранить до момента, когда эта память перестанет быть нужна. И эта память обязана быть явно освобождена с использованием этого указателя! Выделение и освобождение динамической памяти не привязано к времени жизни класса или переменных в функции. Т.е., если Вы выделили память внутри класса (необязательно в конструкторе) и передали указатель на неё за пределы класса (например, в глобальную переменную), то Вы можете освободить память в любое время после деструктора класса.
В Вашем случае указатель никуда не передаётся, а значит - уничтожается вместе с классом. Следовательно, освобождение динамической памяти нужно сделать до уничтожения класса, самый последний шанс - в деструкторе.
 
  • Лойс +1
Реакции: Boroda22 и VVViktor