2013-12-16 2 views
5

Я изучаю C++ с точки зрения развития игры, исходя из долговременного развития на C#, не связанного с игрой, но мне довольно сложно схватить концепцию/использование указателей и удаление ссылок. Я прочитал две главы в учебнике по текущим классам буквально три раза и даже поделился несколькими различными страницами, относящимися к ним, но, похоже, это не так хорошо сочетается.Зачем использовать указатели на C++?

Я думаю, что я получаю эту часть:

#include <iostream> 

int main() 
{ 
    int myValue = 5; 
    int* myPointer = nullptr; 

    std::cout << "My value: " << myValue << std::endl; // Returns value of 5. 
    std::cout << "My Pointer: " << &myValue << std::endl; // Returns some hex address. 

    myPointer = &myValue; // This would set it to the address of memory. 
    *myPointer = 10; // Essentially sets myValue to 10. 

    std::cout << "My value: " << myValue << std::endl; // Returns value of 10. 
    std::cout << "My Pointer: " << &myValue << std::endl; // Returns same hex address. 
} 

Я думаю, что я не получаю, почему? Почему бы просто не сказать myValue = 5, тогда myValue = 10? Какова цель перехода через добавленный слой для другой переменной или указателя? Любой полезный вклад, использование реальной жизни или ссылки на некоторые чтения, которые помогут понять это, будут ВЕЛИКОЕ оценили!

+2

В очень узком случае кода, который вы опубликовали, это нецелесообразно; но пример пытается объяснить вам, как это работает, а не как вы можете использовать его, чтобы делать интересные вещи. Попробуйте добавить еще одну функцию и сыграйте с кодом, чтобы попытаться выяснить, что вы можете сделать. – vanza

ответ

15

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

http://www.cplusplus.com/doc/tutorial/pointers/

Пример: Если у вас есть массив игровых объектов, определенных следующим образом:

std::vector<Entity*> entities; 

И у вас есть класс камеры, которая может «след» конкретного объекта:

class Camera 
{ 
private: 
    Entity *mTarget; //Entity to track 

public: 
    void setTarget(Entity *target) { mTarget = target; } 
} 

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

entities.push_back(new Entity()); 
Camera camera; 
camera.setTarget(entities.front()); 
+0

+1 Единственный ответ до сих пор показывает случай, когда ссылки не будут работать (это потому, что вы предоставляете сеттер, который должен переустанавливать указатель, что невозможно со ссылками). Мне также нравится, что ваш пример связан с развитием игры. :) – leemes

+0

Это хороший пример, потому что он нацелен также на цель OP – streppel

+1

Хотя может быть лучше иметь вектор, содержащий значения, и использовать только указатели, в которых вы ссылаетесь на объекты, которые у вас нет (например, ваш класс камеры) – leemes

1

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

char s[] = "It is pointer?"; 

if (char *p = std::strchr(s, '?')) *p = '!'; 
1

Возьмем, например, указатель на класс.

struct A 
{ 
    int thing; 
    double other; 
    A() { 
     thing = 4; 
     other = 7.2; 
    } 
}; 

Допустим, у нас есть метод, который принимает 'A':

void otherMethod() 
{ 
    int num = 12; 
    A mine; 
    doMethod(num, mine); 
    std::cout << "doobie " << mine.thing; 
} 

void doMethod(int num, A foo) 
{ 
    for(int i = 0; i < num; ++i) 
     std::cout << "blargh " << foo.other; 
    foo.thing--; 
} 

Когда doMethod называется, то A объект передается по значению. Это означает, что объект NEW A создается (как копия). Строка foo.thing-- не будет изменять объект mine вообще, поскольку они представляют собой два отдельных объекта.

Что вам нужно сделать, это передать указатель на исходный объект. Когда вы передаете указатель, тогда foo.thing-- будет изменять исходный объект вместо создания копии старого объекта в новый.

+1

Вы можете либо передать указатель, либо ссылку на функцию. – leemes

1

Мы используем указатели в основном, когда нам нужно динамически выделять память. Например, для реализации некоторых структур данных, таких как Связанные списки, Деревья и т. Д.

1

Если вы передадите значение int по значению, вы не сможете изменить значение вызывающего абонента. Но если вы передадите указатель на int, вы можете его изменить. Это как С измененные параметры. C++ может передавать значения по ссылке, поэтому это менее полезно.

f(int i) 
{ 
    i= 10; 
    std::cout << "f value: " << i << std::endl; 
} 
f2(int *pi) 
{ 
    *pi = 10; 
    std::cout << "f2 value: " << pi << std::endl; 
} 

main() 
{ 
    i = 5 
    f(i) 
    std::cout << "main f value: " << i << std::endl; 
    f2(&i) 
    std::cout << "main f2 value: " << i << std::endl; 
} 

в основном первой печать все еще должна быть 5. Второй должно быть 10.

0

Это делает гораздо больше смысла, когда вы передаете указатель на функцию, см примера:

void setNumber(int *number, int value) { 
    *number = value; 
} 

int aNumber = 5; 
setNumber(&aNumber, 10); 
// aNumber is now 10 

Что мы здесь делаем, это значение *number, это было бы невозможно без использования указателей.

Если вы определили его как это вместо:

void setNumber(int number, int value) { 
    number = value; 
} 

int aNumber = 5; 
setNumber(aNumber, 10); 
// aNumber is still 5 since you're only copying its value 

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

+0

«это было бы невозможно без использования указателей» => Неправильно, вы можете использовать ссылки, и я лично даже считаю это лучше, чем использование указателей для таких случаев. – leemes

+0

Ну, ссылки являются своеобразными указателями? –

+0

Это * вид похожих *, но также очень разные. Ссылки больше похожи на именованные псевдонимы и более высокий уровень; указатели - это адреса памяти и более низкий уровень. Ссылки * всегда * ссылаются на существующий объект, не могут быть повторно закрыты, не могут использоваться для итерации через непрерывный массив элементов, не могут быть нулевыми, ... – leemes

1

С указателем точки C# это то же самое, что и ссылка объекта в C# - это просто адрес в памяти, там хранятся фактические данные, и путем разыменования его вы можете манипулировать этими данными.

Первый из данных не указателя, таких как int в вашем примере, выделен в стеке. Это означает, что тогда он выходит за пределы используемой памяти, будет освобожден. С другой стороны, данные, выделенные оператором new, будут помещены в кучу (точно так же, как тогда вы создадите любой объект на C#), в результате чего эти данные не будут освобождены, если вы потеряете его указатель. Таким образом, используя данные в динамической памяти позволяет вам сделать один из следующих действий:

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

Зачем это необходимо? Есть три основных сценариев использования:

  1. стек памяти быстр, но ограничен, поэтому, если вам нужно хранить большое количество данных, которые вы должны использовать кучного
  2. копирования больших данных вокруг дорого. Затем вы передаете простое значение между функциями в стеке , он копирует. Затем вы передаете указатель, единственное, что скопировано, это просто его адрес (как и на C#).
  3. некоторые объекты в C++ могут быть несъемными, например потоками, например, по своей природе.
+0

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

1

TL; DR: указатели полезны, когда несколько мест должны иметь доступ к той же информации

В вашем примере они не делают много, как вы сказали, что это просто показывает, как они могут быть использованы. Для обозначения узлов, как в дереве, используются указатели на одну вещь. Если у вас есть структура узла, например ...

struct myNode 
{ 
    myNode *next; 
    int someData; 
}; 

Вы можете создать несколько узлов и связать каждую из них next члена предыдущего MyNode в. Вы можете сделать это без указателей, но аккуратная вещь с указателями связана с тем, что все они связаны друг с другом, когда вы передаете список myNode, вам нужно только передать первый (корневой) узел.

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

int a = 5; // set a to 5 
int *b = &a; // tell b to point to a 
int *c = b; // tell c to point to b (which points to a) 

*b = 3; // set the value at 'a' to 3 
cout << c << endl; // this would print '3' because c points to the same place as b 

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

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

int count = 0; 

cout << "How many numbers do you want?\n> "; 
cin >> count; 

// Create a dynamic array with size 'count' 
int *myArray = new int[count]; 

for(int i = 0; i < count; i++) 
{ 
    // Ask for numbers here 
} 

// Make sure to delete it afterwars 
delete[] myArray; 
+0

Кто-то требует времени, чтобы вы поняли. Поэтому вы должны потратить время на чтение. – Giuliano

+0

OP запросил использование в реальном мире, и я представил некоторые, я что-то пропустил? – user2896976

+0

A tl; dr не то, что я отвечаю на людей, когда они пытаются что-то спросить. Даже когда они говорят больше, чем нужно. Это моя точка зрения. – Giuliano

1

Какова цель при переходе через дополнительный уровень для другой переменной или указатель?

Нет ни одного. Это преднамеренно надуманный пример, показывающий вам, как работает механизм.

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

1

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

Shape * myShape = new Circle(); 
myShape->Draw(); // this draws a circle 
// in fact there is likely no implementation for Shape::Draw 

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

0

Хорошо использовать указатели в программировании - довольно красивая концепция. А для динамического выделения памяти важно использовать указатели для хранения адреса первой ячейки памяти, которую мы зарезервировали, и то же самое, что и для освобождения памяти, нам нужны указатели. Это правда, как сказал один из вышеперечисленных ответов, что вы не можете понять использование poinetrs, пока вам это не понадобится. Одним из примеров является то, что u может создавать массив переменных размеров с помощью указателей и динамического выделения. И важно одно: с помощью указателей мы можем изменить фактическое значение местоположения, потому что мы косвенно коснемся местоположения. Более того, когда нам нужно передать нашу ценность по ссылке, есть моменты, когда ссылки не работают, поэтому нам нужны указатели.

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

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