2011-01-21 2 views
2

Я читал некоторые книги, а когда речь заходит о классе/функциях с помощью указателей/динамической памяти (или кучи или w/e они называют это), я начинаю путаться.Динамическая память функций? C++

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

Также ..... когда вы используете их, это другой вопрос.

+5

BASIC не поддерживает указатели: P – Thomas

+2

@Thomas: Я думаю, что он имел в виду понимание основ указателей, а не BASIC. – Chan

+1

У Альфа П. Штайнбаха была книга, которую он писал по этому вопросу, но все ссылки, которые я могу найти, теперь тупики. –

ответ

5

распределение Stack:

char buffer[1000]; 

Здесь 1000 должна быть постоянной. Память автоматически освобождается, когда buffer выходит за рамки.

Heap Allocation:

int bufsz = 1000; 
char* buffer = new char[bufsz]; 
//... 
delete [] buffer; 

Здесь bufsz может быть переменной. Память должна быть явно освобождена.

Когда использовать кучу:

  • Вы не знаете, сколько места потребуется во время компиляции.
  • Вы хотите, чтобы память/объект сохранялась за пределами текущей области.
  • Вам нужен большой кусок памяти (стек пространство является более ограниченным, чем куча пространства)
+0

Хотя все ответы здесь верны, я считаю, что этот вопрос наиболее точно отвечает на вопрос Аскера. –

+0

Я не вижу этого, но не потому, что ответ был неудачным - это так! - но потому, что вопрос довольно неясен. В частности, он не спрашивал о различиях в распределении, а о примерах использования динамической памяти. Отличные догадки, чтобы узнать, что именно нужно Саурону. – foo

0

(не проверял, просто записывая и сохраняя вещи намеренно примитивно, как просили..)

int* oneInt = new int; // allocate 
*oneInt = 10;   // use: assign a value 
cout << *oneInt << endl; // use: retrieve (and print) the value 
delete oneInt;   // free the memory 

теперь массив целых чисел:

int* tenInts = new int[10]; // allocate (consecutive) memory for 10 ints 
tenInts[0] = 4353;   // use: assign a value to the first entry in the array. 
tenInts[1] = 5756;   // ditto for second entry 
//... do more stuff with the ints 
delete [] tenInts;   // free the memory 

теперь классов/объектов:

MyClass* object = new MyClass(); // allocate memory and call class constructor 
object->memberFunction("test"); // call a member function of the object 
delete object;     // free the object, calling the destructor 

Это то, что вы хотели? Я надеюсь, что это помогает.

3

Указатель является в основном переменная, которая содержит адрес памяти другой переменной (или в других случаях функции, но позволяет сосредоточиться на первом).

Это означает, что, если я заявляю int[] x = {5,32,82,45,-7,0,123,8};, что переменная будет выделена память по определенному адресу, позволяет сказать, что он получил выделено на адрес 0x00000100 через 0x0000011F однако мы могли бы иметь переменную, которая указывает на определенный адрес памяти, и мы можем использовать это для доступа к нему.

Итак, наш массив выглядит следующим образом

Address   Contents 
0x00000100  1 
0x00000104  32 
0x00000108  82 
0x0000010B  45 
0x00000110  -7 
0x00000114  0 
0x00000118  123 
0x0000011B  8 

Если, например, мы должны были создать указатель на начало массива мы могли бы сделать это: int* p = &x; представить этот переменный указатель был создан адрес памяти 0x00000120 таким образом память на этом адресе будет содержать ячейку памяти для начала массива x.

Address   Contents 
0x00000120  0x00000100 

Затем можно получить доступ к содержимому по этому адресу через указатель разыменования указателя, так что int y = *p приведет к y = 1. Мы также можем переместить указатель, если мы должны были сделать p += 3;, указатель будет перемещен на 3 адреса вперед (обратите внимание, однако, что он перемещает в 3 раза больше размера типа объекта, на который он указывает, здесь я делаю примеры с помощью 32-битная система, в которой int 32 бита или 4 байта, поэтому адрес будет перемещаться на 4 байта для каждого приращения или всего 12 байтов, чтобы указатель в конечном итоге указывал на 0x0000010B), если бы мы снова разыскали p делая y = *p;, тогда у нас получится y = 45. Это только начало, вы можете делать много вещей с указателями.

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

4

ОЗУ вашего компьютера представляет собой большую кучу байтов, упорядоченных один за другим, и каждый из этих байтов может быть независимо связан по его адресу: целое число начинается с нуля, вверх. Указатель - это просто переменная, которая удерживает этот адрес в одном месте в памяти.

Поскольку ОЗУ представляет собой большой кусок байтов, ЦП традиционно делит эту большую кучу байтов на несколько кусков. Наиболее важными из них являются:

  1. Код
  2. Heap
  3. Stack

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

  • Глобальные переменные
  • Динамические данные, через new операции на C++, или malloc() на С.

стек является кусок память, которая привыкает для хранения:

  • Локальных переменными параметрами
  • функции
  • Возвращаемые значения (оператор возврата на C/C++).

Основное отличие между стеком и кучей заключается в том, как оно используется. В то время как куча представляет собой большой пул байтов, стек «растет», как стопка блюд: вы не можете удалить блюдо на дно, если на нем нет больше блюд.

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

Данные, живущие на стеке, имеют разные «жизненные промежутки», чем данные, живущие на куче. Как только функция завершается, данные о локальных переменных теряются.

Но если вы выделите данные в куче, эти данные не будут потеряны, если вы явно освободите эти данные с помощью операций delete или free().

1

Предупреждение: Не делайте этого. Вот почему у нас есть векторы.

Если вы хотите создать массив данных и вернуться, если от функции, как бы вы это сделали?

Очевидно, что это не работает:

int [10] makeArray(int val) 
{ 
    int arr[10]; 
    for(int i=0; i<10; ++i) 
     arr[i] = val; 
    return arr; 
} 

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

int * makeArray(int val) 
{ 
    int arr[10]; 
    for(int i=0; i<10; ++i) 
     arr[i] = val; 
    return &(arr[0]); // Return the address of the first element. 
         // Not strictly necessary, but I don't want to confuse. 
} 

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

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

int * makeArray(int val) 
{ 
    int * arr = new int[10]; 
    for(int i=0; i<10; ++i) 
     arr[i] = val; 
    return arr; 
} 

Тогда вы можете вызвать эту функцию и использовать этот массив так:

int * a = makeArray(7); 

for(int i=0; i<10; ++i) 
    std::cout << a[i] << std::endl; 

delete [] a; // never forget this. Obviously you wouldn't do it right 
      // away like this, but you need to do it sometime. 

Использование указателей с новыми и дает вам преимущество, что вы можете определить размер массива во время выполнения, что-то вам не может сделать с локальными статическими массивами (хотя вы можете в C):

int * makeArray(int size, int val) 
{ 
    int * arr = new int[size]; 
    for(int i=0; i<size; ++i) 
     arr[i] = val; 
    return arr; 
} 

Это используется быть одной из главных целей для точки ERS. Но, как я сказал наверху, мы больше этого не делаем. Мы используем vector.

Один из последних остатков указателей не для динамических массивов. Единственный раз, когда я их использую, - это классы, в которых я хочу, чтобы один объект имел доступ к другому объекту, не давая ему права собственности на этот объект. Таким образом, Object A должен знать об объекте B, но даже когда объект A отсутствует, это не влияет на объект B. Вы также можете использовать ссылки для этого, но не если вам нужно предоставить Object A возможность изменить какой объект у него есть доступ.

+1

@downvoter: причина? –

+0

Не будьте доктриной об использовании/неприменении языковых функций. –

0

Я думаю, что это то, что вы спрашиваете о:

В основном C++ не позволяет массивы переменных размеров. Любой массив в C++ должен иметь определенный размер. Но вы можете использовать указатели, чтобы обойти это. Рассмотрим следующий код:

int *arry = new int[10]; 

Это просто создали массив целых чисел из 10 элементов, и в значительной степени то же самое точное, что, как это:

int arry[] = int[10]; 

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

Class class: 
{ 
public: 
    void initArry(int size); 

private: 
    int arry[]; 
}; 

void class::initArry(int size) 
{ 
    arry = int[size]; // bad code 
} 

По какой причине C++ был разработан, чтобы не позволить регулярные массивы быть назначены размеры, которые определяются во время выполнения. Вместо этого они должны быть присвоены размерам при кодировании. Однако другой способ сделать массив в C++ - использование указателей - не имеет эту проблему:

Class class: 
{ 
public: 
    ~class(); 
    void initArry(int size); 

private: 
    int *arry; 
}; 

class::~class() 
{ 
    delete []arry; 
} 

void class::initArry(int size) 
{ 
    arry = new int[size]; // good code 
} 

Вы должны сделать некоторые очистки памяти во втором примере, следовательно, почему я включил деструктор, но с использованием указателей таким образом вы можете определить размер массива во время выполнения (с переменным размером). Это называется динамическим массивом, и говорят, что память здесь распределяется динамически. Другой тип - статический массив.

Что касается 2-мерных массивов идут, вы можете справиться с этим вроде этого:

Class class: 
{ 
public: 
    ~class(); 
    void initArrays(int size1, int size2); 

private: 
    int **arry; 
}; 

class::~class() 
{ 
    delete [] arry[0]; 
    delete [] arry[1]; 
    delete [] arry; 
} 

void class::initArrays(int size1, int size2) 
{ 
    arry = new int*[2]; 
    arry[0] = new int[size1]; 
    arry[1] = new int[size2]; 
} 

Отказа от ответственности, хотя: Я не делал много с этим языком на некоторое время, так что я может быть немного некорректно для некоторого синтаксиса.

Смежные вопросы