2013-06-05 2 views
5

Известно, что определение переменной кучи с new получает указатель без указания имени:Объявите переменную стека без указания имени и получить указатель

Var *p = new Var("name", 1); 

Но я должен очистить переменную, на которую указывает p с delete p позже в программе.

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

Var v("name", 1); 
Var *p = &v; 

довольно утомительно, и спецификатор v никогда не будет ссылки.

Могу ли я объявить экземпляр класса стека и получить его указатель без указания его имени?

+0

'Var * p = Var (" name ", 1);' не работает? – Patashu

+7

Ум, вам действительно нужно получить доступ к объекту с помощью указателя? –

+5

@Patashu Um .. no. – 0x499602D2

ответ

10

Здесь есть два вопроса. Первый:

Var *p = new Var("name", 1); 

But I have to clear the variable pointed to by p with delete p later on in the program.

I want to declare a stack variable so it is automatically cleared after function exits

Так вот, вы спрашиваете, как выделить память без явного очистить его впоследствии. Решение состоит в использовании std::unique_ptr:

std::unique_ptr<Var> p(new Var("name", 1)); 

Voila! unique_ptr автоматически очистится, у него практически нет накладных расходов по сравнению с необработанным указателем, и он перегружает операторы * и ->, поэтому вы можете использовать его так же, как необработанный указатель. Найдите «умные указатели C++ 11», если вы хотите узнать больше.

Второй вопрос:

I only want to get the pointer, and the following:

Var v("name", 1); 
Var *p = &v; 

is quite tedious, and specifier v will never be referenced.

Важным моментом здесь является то, что Var *p = &v является совершенно ненужным. Если у вас есть функция, которая требует указатель, вы можете использовать &v на месте:

void SomeFunc(const Var* p); 
// ... 
Var v("name", 1); 
SomeFunc(&v); 

Там нет необходимости ставить & V в отдельной переменной перед передачей его в функцию, которая требует указатель.

Исключение, если функция принимает ссылку на указатель (или указатель на указатель):

void SomeFunc2(Var*& p); 
void SomeFunc3(Var** p); 

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

+0

Один nitpick: вы должны, вероятно, уточнить, что объект, на который указывает 'unique_ptr', здесь * не * в стеке; это в куче. Конечно, жалоба OP на кучу была только тем, что исходные указатели не выполняют автоматическую очистку, что, конечно же, решается с помощью 'unique_ptr', но все же стоит знать, что память кучи выделяется и освобождается здесь. Кроме того, он * нельзя использовать * так же, как и необработанный указатель, потому что для передачи его функции, которая на самом деле берет необработанный указатель (который, как вы указываете, вероятно, является примером использования ОП здесь), вы должен использовать 'get()'. –

+0

(Лично я [не продается на 'get()'] (http://stackoverflow.com/q/28952141/1858225), хотя из голосов по этому вопросу кажется, что большинство людей не согласны со мной в этом вопросе .) –

+0

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

2

Невозможно сделать это, выделив стек. Тем не менее, вы можете использовать std::make_shared для кучи:

#include <memory> 

std::shared_ptr<Var> p = std::make_shared<Var>(); 
+1

Который является полностью более элегантным, чем то, что OP уже имеет и определенно менее утомительно с точки зрения ввода;) – JustSid

+6

Это выделяет в свободном хранилище (кучу) не стек. –

+0

ничего себе, так сложно! – CDT

1

В стоимость/риск быть более запутанной, вы можете избежать повторения типа в вашем коде в вопросе ала:

Var v("name", 1), *p = &v; 

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

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

+0

СВЯТОЙ КРАПУ, КОТОРЫЙ БРОКЕН. alloca достаточно опасен, в первую очередь, без людей, возвращающих указатели на бит стека, который он дал вам внутри конструктора. Святое дерьмо! – kfsone

+2

@kfsone: да ... yikes ... WTF я думал ??? –

+0

tbh, сделал мой день =) – kfsone

-2

Да, можно вернуть адрес во временный (то есть стек) объект и назначить его указателю. Однако компилятор может фактически отказаться от объекта (т. Е. Вызвать перезапись раздела в памяти) до конца текущей области. (TO CLARIFY: ДАННЫЕ СРЕДСТВА НЕ ДЕЛАЙТЕ ЭТО. КОГДА-ЛИБО.) См. Обсуждение в комментариях ниже о поведении, наблюдаемом в разных версиях GCC в разных операционных системах. (Я не знаю, указывает ли факт, что версия 4.5.3 дает предупреждение вместо ошибки, указывает, что это всегда будет «безопасным» в том смысле, что указатель будет действительным всюду в пределах текущей области, если вы скомпилируете с этой конкретной версии GCC, но я бы не стал на это рассчитывать)

Вот код, который я использовал (модифицированный согласно предложению Джонатана Леффлера).

#include <stdio.h> 

class Class { 
public: 
    int a; 
    int b; 
    Class(int va, int vb){a = va; b = vb;} 
}; 

int main(){ 
    Class *p = &Class(1, 2); 
    Class *q = &Class(3, 4); 
    printf("%p: %d,%d\n", (void *)p, p->a, p->b); 
    printf("%p: %d,%d\n", (void *)q, q->a, q->b); 
} 

при компиляции с помощью GCC 4.5.3 и запустить (на Windows 7 SP1), этот код напечатал:

0x28ac28: 1,2 
0x28ac30: 3,4 

При компиляции с помощью GCC 4.7.1 и запустить (на Mac OS X 10.8.3), он напечатал:

0x7fff51cd04c0: 0,0 
0x7fff51cd04d0: 1372390648,32767 

В любом случае, я не уверен, почему вы просто не просто объявляете переменную нормально и используете &v везде, где вам нужно что-то «похожее на указатель» (например, в функциях, требующих указателя в качестве аргумента).

+1

Когда я скомпилирую ваш код в файле 'clash.cpp', GCC 4.7.1 (' g ++ -Wall -Wextra' говорит: 'clash.cpp: In function 'int main()':' и 'clash.cpp: 11: 26: ошибка: с адресом временного [-fpermissive]'. Обратите внимание, что это ошибка с 4.7.1 (в какой версии вы использовали?) Я думаю, что он пытается сказать вам, что продолжительность временного является завершением инструкции, поэтому указатель ничего не указывает после завершения инструкции. для компиляции мне пришлось добавить '-fpermissive'. –

+0

Когда я запускаю этот незначительный вариант вашего кода:' int main() { Класс * p = & Класс (1, 2); Класс * q = & Класс (3, 4); printf ("% p:% d,% d \ n", (void *) p, p-> a, p-> b); printf ("% p:% d,% d \ n", (void *) q, q-> a, q-> b); } 'Я получаю вывод мусора. Без печати '% p' (несколько до удивления) код напечатал' 1,2' и '3,4', но с ним я получил:' 0x7fff51cd04c0: 0,0' и '0x7fff51cd04d0: 1372390648, 32767'. (GCC/G ++ 4.7.1 на Mac OS X 10.8.3) –

+0

@JonathanLeffler Wacky. Я использую 4.5.3, и это определенно не ошибка в этой версии. Я получаю '0x28ac28: 1,2' и' 0x28ac30: 3,4'. –

0

Я не думаю, что есть способ преодолеть это без некоторых накладных расходов (например, shared_ptr). поэтому самый короткий способ написать это будет:

Var v("name", 1), *p = &v; 
Смежные вопросы