Что безопасно? возврат структуры или указателя из функции

#include <iostream>

struct person_t{
        int age;
};

person_t get_person1(){
        person_t person;
        person.age = 10;
        return person;
}

person_t * get_person2(){
        person_t *person = new person_t;
        person->age = 20;
        return person;
}

int main(){
        person_t person1 = get_person1();
        person_t *person2 = get_person2();
        std::cout << person1.age << std::endl;
        std::cout << person2->age << std::endl;
        delete person2;
        return 0;
}

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

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

Но когда я ищу "Как вернуть структуру из функции С++", он предлагает мне метод one (с get_person1()) (Пример здесь). Но я думаю, что этот метод уничтожит объект после вызова функции, и я думаю, что метод 2 является самым безопасным.

Я здесь не прав...? Или любое мнение по этой теме..?

Спасибо!

Ответ 1

Используйте возврат по значению по трем причинам:

  • Это делает ваш код доступным для чтения.
  • Ваша структура здесь мала (один int), поэтому она похожа на возвращение int. Вы можете сделать это на С++, это эффективно
  • В С++ 17 (и для большинства компиляторов до этого) будет исключена стоимость промежуточной копии объекта. Он известен как RVO. Вы должны поместить только одно возвращение в свою функцию get_person.

Ответ 2

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

Что разрушается, это локальный объект (т.е.: person внутри get_persion1()). Возвращается копия этого объекта: копируется struct person_t (также может быть перемещена). Таким образом, это безопасно.


get_person2() также безопасен, но рассмотрите использование интеллектуальных указателей вместо необработанных указателей:

std::unique_ptr<person_t> get_person2(){
        auto person = std::make_unique<person_t>();
        // For pre-C++14
        // std::unique_ptr<person_t> person(new person_t);
        person->age = 20;
        return person;
}

Таким образом, вызывающему абоненту get_person2() не нужно вызывать delete (он может забыть это сделать).

Ответ 3

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

Основное отличие состоит в том, что первый подход копирует struct, а второй - копирует указатель на struct. Когда struct является крошечным, например, в вашем примере, нет никакой разницы. Когда значение struct становится большим, возврат указателя может сэкономить вам несколько циклов процессора за счет дополнительного распределения памяти, поэтому это далеко не гарантированный выигрыш.

Очевидно, что второй подход имеет еще один недостаток в том, что в конце концов delete struct. Первый подход свободен от этого требования.

Ответ 4

Оба метода безопасны.

Метод 1 безопасен, поскольку локальный person скопирован из области действия. Часто, чтобы избежать копирования, можно использовать Оптимизацию возвращаемого значения (RVO).

Метод 2 также безопасен, поскольку память для экземпляра person выделяется в куче. Это распределение очевидно справедливо вне сферы действия функции. Тем не менее, вы должны помнить о том, чтобы освободить память, когда вы закончите ее использование. Это не критично в вашем примере кода. Куки-распределения автоматически освобождаются при завершении main, поэтому для вашего короткого примера вам действительно не нужно delete person2.

Вопрос в том, какой из указанных методов "безопасен". Это невозможно ответить объективно, хотя я бы утверждал, что большинство программистов на C++ будут советовать против метода 2 в пользу метода 1. Многие программисты на C++ рекомендуют использовать интеллектуальные указатели.

Заключительное замечание: Метод 1 и метод 2 принципиально разные. Выделение памяти в стеке (метод 1) по сравнению с распределением памяти в куче (метод 2) - это две разные концепции. Есть еще много соображений, кроме соображений безопасности.