Как я могу вернуть const ref a локальной переменной в C ++? - PullRequest
4 голосов
/ 12 марта 2009

У меня есть функция, для которой я не могу изменить параметры функции. Мне нужно вернуть константную ссылку на std :: string, созданную в этой функции. Я пытался использовать boost shared_ptr, но это не работает. Зачем? Как мне сделать эту работу?

const std::string& getVal(const std::string &key) {
  boost::shared_ptr<std::string> retVal(new std::string());
  ... //build retVal string with += operator based on key
  return *retVal;
}

Ответы [ 6 ]

3 голосов
/ 12 марта 2009

Вам нужно будет вернуть boost shared_ptr, а не std :: string. Как только функция выйдет, этот shared_ptr в стеке выйдет из области видимости и, поскольку есть только одна ссылка, он будет удален. Вам также необходимо вернуть копию, а не ссылку.

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

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

1007 * Е.Г. *

const std::string& getVal(const std::string &key) {
  return *(new std::string("Something"));
}

// get the val, remember we must delete this pointer!
std::string* pString = &getVal("SomeKey");
delete pString;
3 голосов
/ 12 марта 2009

Вы не можете вернуть ссылку на локальную переменную из функции с c ++. Хотя в c ++ 0x это возможно.

Выделите строку в куче и очистите вручную позже:

Если вы не можете изменить интерфейс функции, вам нужно будет создать ее в куче, а затем вручную удалить ее.

//Remember to free the address that getVal returns
const std::string& getVal(const std::string &key) {
  std::string *retVal = new std::string();
  ... //build retVal string with += operator based on key
  return *retVal;
}

То же решение, но не вручную:

Поскольку вышеизложенное в конечном итоге приведет к утечке памяти, если вы забудете ее освободить вручную. Я бы порекомендовал обернуть этот вызов в класс и использовать RAII. То есть в конструкторе вызовите getVal и установите член этого класса так, чтобы он указывал на него. В деструкторе вашего класса вы бы удалили его.

Почему код, который вы дали с shared_ptr, не работает:

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

2 голосов
/ 12 марта 2009

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

0 голосов
/ 12 марта 2009

Еще один момент, на который следует обратить внимание: стандарт позволяет компилятору оптимизировать удаленные копии, если это возможно. Стандарт ссылается на это копия elision , однако большинство людей знают это как «Оптимизация именованных возвращаемых значений», см. здесь .

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

class A {};

A foo () {
  A a;          // #1
  return a;
}

void bar () {
  A b = foo();  // #2
}

В приведенном выше примере «a» будет создан в том же месте, что и «b» на панели, и поэтому, когда функция возвращает, копия вообще не требуется. В заключение, возможно, стоит проверить ваш компилятор, чтобы увидеть, выполняет ли он эту оптимизацию. Стандартная ссылка для этого - 12,8 / 15, если вам интересно.

0 голосов
/ 12 марта 2009

Так как вам нужна ссылка на константу, вы можете вернуться по значению и заставить вызывающего получить ее с ссылкой на константу.

Травяной Саттер GotW 88

0 голосов
/ 12 марта 2009

Поскольку вы не можете изменить сигнатуру функции, вы можете вернуть ссылку на статическую переменную:

const std::string& getVal(const std::string &key) {
    static std::string retVal;
    ... //build retVal string with += operator based on key
    return retVal;
}

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

Если ваше приложение не является многопоточным, и каждый вызов getVal () немедленно использует или копирует строку, то вы, вероятно, можете избежать этого. Но я бы настоятельно рекомендовал просто вернуть std :: string.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...