Правильно ли я инициализирую свои ссылочные переменные C++?

Я попытался решить эту проблему в Google, и не могу найти ничего, что мне кажется актуальным. Поэтому я должен искать не ту вещь; тем не менее, я буду признателен за несколько советов...

Foobar &foobar = *new Foobar(someArg, anotherArg);

Это только у меня так или кажется вонючим?

Я понимаю что new Ключевое слово предназначено для использования с указателями (как таковые):

Foobar *foobar = new Foobar(someArg, anotherArg);

Но что, если вам не нужен указатель на этот экземпляр, и вместо этого вы хотите использовать ссылку? Или это тот случай, когда вам не нужно явно инициализировать его (так же, как локальные переменные); и если это так, что если я хочу инициализировать с параметрами?

Следующее не работает (если я не делаю что-то не так):

// the last argument is of type: int
Foobar &foobar(someArg, anotherArg);

... выдает ошибку компилятора:

список выражений инициализатора, обработанный как составное выражение, недопустимая инициализация неконстантной ссылки типа 'Foobar&' из временного типа 'int'

И также это не похоже на работу:

Foobar &foobar = Foobar(someArg, anotherArg);

... выдает ошибку компилятора:

ошибка: неверная инициализация неконстантной ссылки типа 'Foobar&' из временного типа 'Foobar'

Обновление 1:

Имейте в виду, что я возвращаю это значение, поэтому я не хочу использовать локальную переменную; Я хочу использовать значение в куче, а не в стеке:

Foobar &helloWorld()
{
    Foobar &foobar = *new Foobar(someArg, anotherArg);
    foobar.HelloWorld();
    return foobar;
}

Должен ли я просто использовать указатели вместо этого, или это полностью допустимо?

6 ответов

Решение

Почему вы думаете, что вам нужно использовать новые и ссылки вообще? Почему бы и нет:

Foobar foobar(someArg, anotherArg);

Для вашей функции - вернуть значение:

Foobar helloWorld()
{
    Foobar foobar(someArg, anotherArg);
    foobar.HelloWorld();
    return foobar;
}

или указатель:

Foobar * helloWorld()
{
    Foobar * foobar = new Foobar(someArg, anotherArg);
    foobar->HelloWorld();
    return foobar;
}

Если вы сделаете это - вызывающий отвечает за удаление выделенного объекта в какой-то момент.

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

Да, это вонючий!

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

Ты можешь сделать...

const Foobar &foobar = Foobar(someArg, anotherArg);

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

Foobar foobar(someArg, anotherArg);

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

Ссылки в C++ на самом деле не так сильны, как того ожидают люди, я думаю, что путаница исходит от людей, которые привыкли к языкам, таким как Java и C#, которые не имеют указателей и имеют ссылки, которые можно переназначать и использовать.

Ссылка в C++ обычно лучше всего использовать в качестве псевдонима для переменной, так что вы можете упростить такие вещи, как передача параметров и возвращаемые значения. Есть очень немного ситуаций, когда вы пытаетесь получить ссылку, как вы делаете в первой строке. Поэтому обычно вам не нужно делать то, что вы пытаетесь сделать:)

Вторая строка, конечно, правильная, и вы можете сделать что-то вроде return *foobar из функции, которая возвращает ссылку.

Вы пишете это правильно. Но имейте в виду, что странно освобождать ptr с помощью delete &refVar; (это может быть ошибочно принято за переменную, которая не была создана new).

Вы должны проверить GotW на предмет хороших практик, проходящих вокруг http://www.gotw.ca/gotw/ Я не помню, какой это был урок (их было больше одного), но чтение этого является более ценным, чем кто-либо понимает (уловки будут меньше сюрпризов)

Вы должны попробовать написать код, который НИКОГДА не использует указатели. Я делаю это, но однажды застрял, когда мне был нужен контейнер BaseObj. Там не было работы вокруг этого. Обычно я использовал контейнеры STL и большинство переменных в стеке { MyClass myVar; ... } или как член и раздайте его по мере необходимости.

Это тихо, легко удалить указатели, как только вы начнете передавать ссылку вместо указателей и использовать контейнеры stl вместо новых. Обратите внимание, что я никогда не использую auto_ptr. vector, string, deque, list и map должны делать большую часть того, что вам нужно. Есть другие контейнеры.

Я бы порекомендовал использовать умный указатель. Вы должны управлять собственностью, и ссылка не сделает этого за вас. На самом деле, для вызывающего абонента будет неясно, есть ли какие-либо проблемы с собственностью. Как отметил Скотт Лэнгхэм в комментарии, std::auto_ptr будет работать. Использование shared_ptr из Boost или TR1 может быть лучшей идеей.

boost::shared_ptr<Foobar> helloWorld()
{
    boost::shared_ptr<Foobar> foobar(new Foobar(someArg, anotherArg));
    foobar->HelloWorld();
    return foobar;
}

Нет, у вас есть это. Foo& "указывает" на реальный объект, поэтому, если вы действительно хотите ссылку на foo, вы должны разыменовать указатель foo.

@Neil имеет смысл, хотя - синтаксически это то, как вы получаете то, что вы хотите, но почему вы этого хотите?

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