Возвратите объект с 2 дополнительными ctors в функции - PullRequest
0 голосов
/ 31 мая 2018

У меня есть функция в c ++ с двумя необязательными c'or объекта, который я построил (один с чем-то в векторе "vals", а другой без).

...
    RecievedMessage a(sc, type);
    if (!vals.empty()){
        //a.~RecievedMessage();
        RecievedMessage a(sc, type, vals);
    }
    return &a;
}

строка в // этонеобязательно.

Будет ли это работать (с дополнительной строкой или без нее)?Зачем?Если нет, как это исправить без установщика для "vals"?Большое спасибо.

Ответы [ 3 ]

0 голосов
/ 31 мая 2018

Есть три основных проблемы с вашим кодом прямо сейчас:

Прежде всего, ваш закомментированный вызов деструктора ~ReceivedMessage() вообще не должен быть там.В C ++ деструктор объектов автоматически вызывается, когда заканчивается время жизни объекта (либо когда он выходит из области видимости, либо когда вызывается delete, если он был динамически выделен с помощью new).Хотя в некоторых ситуациях необходим явный вызов деструктора (например, «размещение нового»), с этими ситуациями вы вряд ли столкнетесь.

Во-вторых, ваше объявление RecievedMessage a(sc, type, vals); во внутреннем if не заменяет значение a во внешней области видимости.Это просто создает другую переменную с тем же именем, которая затеняет внешний a, тогда как return &a; во внешней области видимости может ссылаться только на внешний a.Внутренний a больше не существует в этой точке, поскольку он вышел из области видимости.

Чтобы решить эту проблему, вместо этого назначьте новое значение для a с помощью оператора = исоздание временного ReceivedMessage:

if (!vals.empty()) {
    a = ReceivedMessage(sc, type, vals);
}

Это должно работать до тех пор, пока для ReceivedMessage.

определено правильное operator= (неявно или иначе). В-третьих, ваша функциявозвращая указатель на локальную переменную a.Поскольку объекты в C ++ уничтожаются, как только они выходят из области видимости, a больше не существует к моменту возврата функции, поэтому указатель ReceivedMessage *, получаемый вызывающим кодом, является недопустимым, и было бы неопределенным поведение для разыменования, чтоуказатель и используйте его.

Существует несколько исправлений этой проблемы:

Первый вариант - вместо возврата указателя (ReceivedMessage *), просто вернуть ReceivedMessageпо значению.

ReceivedMessage foo()
{
    ReceivedMessage a(123);

    return a;
}

Это должно работать до тех пор, пока для ReceivedMessage.

определен правильный конструктор копирования или перемещения (неявно или иным образом). Второй вариант - использоватьstd::unique_ptr, и вместо этого сделайте так, чтобы ваша функция возвращала std::unique_ptr<ReceivedMessage>.

#include <memory>

std::unique_ptr<ReceivedMessage> foo()
{
    std::unique_ptr<ReceivedMessage> a;

    if (vals.empty()) {
        a = std::make_unique<ReceivedMessage>(sc, type);
    } else {
        a = std::make_unique<ReceivedMessage>(sc, type, vals);
    }

    return a;
}

Преимущество этого подхода в том, что unique_ptr обнуляем, поэтому вы можете создать ноль unique_ptr без необходимости создаватьReceivedMessage прямо сейчас.Кроме того, вы можете безопасно перемещать и назначать значения unique_ptr, не указав правильный operator= или правильный конструктор копирования / перемещения.

Код вызова может выглядеть следующим образом при использовании unique_ptr:

std::unique_ptr<ReceivedMessage> message = foo();
foo->bar();

в противоположность следующему при использовании ReceivedMessage напрямую:

ReceivedMessage message = foo();
foo.bar();
0 голосов
/ 31 мая 2018

Ваш код повсюду, но я думаю, что вы ищете что-то вроде этого:

ReceivedMessage *MakeReceivedMessage (foo sc, bar type, vector<whatever>& vals)
{
    if (vals.empty())
        return new ReceivedMessage (sc, type);

    return new ReceivedMessage (sc, type, vals);
}

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

Пример использования (для управления временем жизни возвращаемого объекта):

std::unique_ptr<ReceivedMessage> MyReceivedMessage (MakeReceivedMessage (...));
MyReceivedMessage->DoFunkyStuffWithMessage ();
....

Или, как указывает Мельпомена, вы можете вернуть std::unique_ptr<ReceivedMessage> в первую очередь.Некоторые (многие?) Предпочли бы это.Вы можете создать его с помощью std :: make_unique .

0 голосов
/ 31 мая 2018

Нет, это не сработает.

    RecievedMessage a(sc, type);
// Here we construct 'a'
    if (!vals.empty()){
        //a.~RecievedMessage();
// If we enable this line, we destroy 'a'
        RecievedMessage a(sc, type, vals);
// Here we construct a second 'a' that only exists in this block
    }
// End of block: The inner 'a' is destroyed here automatically
    return &a;
}
// End of block: The outer 'a' is destroyed here, again.

Уничтожение объекта дважды имеет неопределенное поведение.Вы не хотите этого.

Если вы не вызываете деструктор вручную, внешний a уничтожается только один раз, что хорошо.

Но в любом случае RecievedMessage a(sc, type, vals); не имеет ничего общего с внешней a и просто создает другую переменную.

Были бы способы обойти это, но последняя строка вашего кода делает все это бессмысленным:

    return &a;

Вы возвращаете адрес локальной переменной.Это само по себе не работает: когда функция возвращается, все ее локальные переменные уничтожаются автоматически, поэтому вы возвращаете неверный указатель.

...