C ++ способы передать указатель на временный объект (в куче) в функцию? - PullRequest
1 голос
/ 09 августа 2011

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

class A { /** stuff */ };
class B : public A { /** stuff */ };

void doSomething( const A* const _p ) { /** stuff */ }

void callingRoutine()
{
  A* tempPointer = new B;
  doSomething( tempPointer );
  delete tempPointer;
}

Теперь, поскольку мне действительно нужен объект типа B в вызове doSomething, есть ли способ сделать это в одной строке? Делая

doSomething( new B );

создает утечки памяти (так говорит Вальгринд). Или будет

doSomething( &B );

быть рекомендованным способом? Последний компилируется, но выдает предупреждения о передаче указателей на временные объекты. Это то, что я хочу сделать, но будет ли так безопасно?

Ответы [ 8 ]

5 голосов
/ 09 августа 2011

Если вы можете изменить сигнатуру вызываемой функции, измените ее на:

void doSomething( A const& obj );

Тогда вы можете позвонить с помощью:

doSomething( B() );

Если вы не можете, следует объявить локальную переменную:

void callingRoutine()
{
    B temp;
    doSomething( &temp );
}

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

void callingRoutine()
{
    {
        B temp;
        doSomething( &temp );
    }
    //  More code here...
}

В общем, однако, если это необходимо, ваши функции вероятно, слишком долго.

4 голосов
/ 09 августа 2011

Самый чистый способ - сделать

B b;
doSomething(&b);

Но что вы действительно должны написать, зависит от того, что делает функция doSomething.Если нормально уничтожить b в конце callingRoutine, то это более быстрый и чистый способ сделать это, потому что распределение в стеке происходит быстрее, чем new и не требует последующего удаления b.

3 голосов
/ 09 августа 2011

Я немного удивлен, что никто не указал на очевидное:

void callingRoutine()
{
    doSomething(&B());
}

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

3 голосов
/ 09 августа 2011

В вашем случае вы можете использовать автоматическую переменную (в стеке)

void callingRoutine()
{
  B obj;
  doSomething( &obj );
}  // obj is destroyed automatically

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

См. эту ссылку со страницы Бьярне, где он указал точный пример как ваш.

Кстати, если вам нужна только одна строка, тогда решение не будет чистым, но вот обходной путь:

B obj; doSomething( &obj );
1 голос
/ 09 августа 2011

Самый простой способ решить вашу проблему - изменить код следующим образом:

void callingRoutine()
{
    B temp;
    doSomething( &temp );
}

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

1 голос
/ 09 августа 2011

что-то вроде ниже может быть?

void doSomething( std::auto_ptr<A> _p ) { /** stuff */ }

void callingRoutine()
{
  doSomething(std::auto_ptr<A>(new B));
}
0 голосов
/ 09 августа 2011

Вы можете привести временное значение (r-значение) к l-значению на время вызова или выражения:

template<class T>
inline T& l_value(T const& t)
{
    return const_cast<T&>(t);
}

struct A {};

void doSomething(const A*);

void foo()
{
    doSomething(&l_value(A()));
}
0 голосов
/ 09 августа 2011

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

...