Динамическая функция памяти? C ++ - PullRequest
2 голосов
/ 21 января 2011

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

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

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

Ответы [ 6 ]

5 голосов
/ 21 января 2011

Распределение стека:

char buffer[1000];

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

Распределение кучи:

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

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

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

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

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

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

  1. Код
  2. Куча
  3. Стек

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

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

Стек - это фрагмент памяти, который используется для хранения:

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

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

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

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

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

3 голосов
/ 21 января 2011

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

Это означает, что если я объявлю 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. Это только начало, вы можете многое сделать с помощью указателей.

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

1 голос
/ 21 января 2011

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

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

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

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;
}

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

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

0 голосов
/ 22 января 2011

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

В основном 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
}

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

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

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];
}

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

0 голосов
/ 21 января 2011

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

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

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

...