Каков пример использования указателей c ++? - PullRequest
3 голосов
/ 02 июля 2010

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

Ответы [ 10 ]

9 голосов
/ 02 июля 2010

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

// TODO: Remember to call DeleteObjects() when you're done here!!
std::vector<MyObject*> Objects;

void Test()
{
    MyObject *const pObject = new MyObject();
    Objects.push_back(pObject);
}

void DeleteObjects()
{
    std::vector<MyObject*>::iterator it = Objects.begin(), itEnd = Objects.end();
    for (; it != itEnd; ++it)
    {
        delete *it;
    }
    Objects.clear();
}
3 голосов
/ 02 июля 2010

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

Скажем, например, структура данных двоичного дерева, где каждый узел имеет указатели на свои левое и правое поддеревья, гделибо может указывать на 0 (или NULL, что означает недопустимый указатель), чтобы указать, что там нет поддерева.Эта структура может выглядеть так (не очень C ++ - у, но это другая история)

struct TreeNode
{
  TreeNode* left;
  TreeNode* right;
}

Вы не можете использовать что-либо, НО указатель в этом случае, так как это будет бесконечно большая структура.

1 голос
/ 02 июля 2010

Простой пример использования указателей в связанных списках. Больше информации о Википедии .

1 голос
/ 02 июля 2010

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

1 голос
/ 02 июля 2010

Указатели полезны, когда вам нужна функция, которая возвращает более одной переменной.В качестве примера рассмотрим, что вы делаете покупки в продуктовом магазине.Каждый продукт имеет название и цену.Имя будет строкой, а цена - двойной.Если существует функция с именем «покупка», и вы хотите вернуть имя и цену товара, вы можете использовать указатель.

1 голос
/ 02 июля 2010
  1. Загрузка нескольких данных из функции.Вызывающая сторона предоставит адреса областей памяти, которые будут перезаписаны функцией
  2. Динамическое выделение памяти.Распределители возвращали бы указатели на вновь выделенные объекты.
  3. Передача аргументов массива: передача адреса вместо копирования, чтобы сохранить производительность для постоянных данных.
0 голосов
/ 02 июля 2010

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

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

Ужасное использование указателей

Вы должны изучить RAII : этот пример совершенно небезопасен перед лицомисключение

// BAD
void func(size_t n)
{
  int* array = new int[n];
  // .. use array
  delete[] array;
}

// GOOD
void func(size_t n)
{
  std::vector<int> array(n, 0);
  // .. use array
}

Правило большого пальца: если вы видите delete, вы делаете это неправильно.Скорее всего, если вы видите new тоже, хотя это не так верно из-за проблем с переадресацией аргументов.

Используйте ссылки, когда это возможно

// BAD: Contract: do not pass NULL
void func(int* i);

// GOOD
void func(int& i);

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

Хорошее использование указателей :

Псевдоним

void printSorted(std::vector<BigType> const& values)
{
  std::vector<BigType*> references = from(values);

  std::sort(references.begin(), references.end(), ByPointer());

  std::transform(references.begin(), references.end(),
                 std::ostream_iterator<BigType>(std::cout, " "),
                 Dereference());
}

Необязательный результат

Object* find(Key const& key);

это эквивалентно:

boost::optional<Object&> find(Key const& key);

, но довольно менее многословно.

Метод клонирования

Использование оголенного указателя в качестве типа возврата метода clone предписано BoostКлонируемая концепция:

struct Base
{
  virtual ~Base() {}

  virtual Base* clone() const = 0;
};

Есть веская причина: использовать ковариантные типы возвращаемых данных для перегрузки виртуальных методов.Это позволяет нам писать:

struct Derived: Base
{
  virtual Derived* clone() const { return new Derived(*this); }
};

Таким образом, при клонировании из Derived const& в полной мере используется тот факт, что мы знаем, что возвращаемое значение равно по крайней мере a Derived.

К сожалению, программист должен позаботиться о выделенной памяти, поэтому он должен использоваться вместе с Smart Containers:

std::unique_ptr<Base> u = derived.clone();
0 голосов
/ 02 июля 2010

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

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

Итак, когда вы создаете указатель, например:

int* drawer = 0;

Вы ссылаетесь наящик, который помечен номером 0 и содержит целочисленное значение, теперь вы можете подумать, хорошо, но как я могу получить это значение?Ну, это просто:

int value = *drawer;

Таким же образом вы можете сохранить новое значение в этом ящике (адрес памяти):

*drawer = 15;

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

int* drawer = 0;
int* drawer_copy = drawer;
*drawer = 15;

И что происходит?Тот «ящик_копии», который ссылается на адрес 0 как «ящик», позволяет получить доступ к целочисленному значению 15.

Мы также можем сохранить адрес обычной переменной, мы используем префикс «&»чтобы получить этот адрес:

int value = 15;
int* drawer = &value;

Если мы сделаем это сейчас:

value = 5;

"* ящик" вернет 5.

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

0 голосов
/ 02 июля 2010

Какой пример использования C ++ указатели?

Указатели решают следующие проблемы:

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

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

  • запоминание адресов функций (членов) для отложенных вызовов (и обратных вызовов).

  • выделение памяти, которая превышает ее текущую область (она все еще выделяется после завершения области).

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

Иногда указатели также используются в качестве ручек. То есть, если вы хотите позволить клиентскому коду однозначно идентифицировать кусок данных, не заботясь (или не зная), что это за данные, вы приводите адрес данных (указатель) к типу int / некоторому другому типу и передаете его как ручка Это обычно встречается в API, которые предлагают дескрипторы клиентского кода, но не разрешают клиентскому коду доступ к реальным данным (см. Использование WinAPI HANDLE, HWND и т. Д. - это указатели во внутренней реализации, но вы не знаете, - или все равно - каковы фактические данные для их использования).

0 голосов
/ 02 июля 2010
void print_values(int* iptr, int size)
{
    int i;
    for (i=0; i < size; i++)
    {
        printf("%d ", *(iptr++));
    }
    printf("\n");
}

int main()
{
    int table[256];
    memset(table, 0, sizeof(table));
    print_values(table, sizeof(table)/sizeof(int));
}

Или как массив функций (пример):

#define ___FUNC_B { func_1, func_2, func3 }
void ( __closure __fastcall * __FUNC_B [__FUNC_MAX] )( TObject* ) = ___FUNC_B;

Использование объектов по указателям во многих случаях лучше:

CClass *ptr = new CClass();
/* something */
delete ptr;

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

vector <CClass*> Vptr;
for (i=0; i < 100; i++)
{
    Vptr.push_back(new CClass());
}

sort(Vptr.begin(), Vptr.end(), __cmp_func);
/* something */

while (!Vptr.empty())
{
    delete *(Vptr.begin());
    Vptr.erase(Vptr.begin());
}

Для динамического выделения памяти на языке C:

char *memory = (char*) malloc(1024);
if (memory == NULL)
{
    exit(1);
}

/* you have alocated 1KB in memory */
memory = (char*) realloc(2048);
if (memory == NULL)
{
    exit(1);
}

/* you have alocated 2KB in memory */
free(memory);
/* you have nothing */
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...