С++: Каковы сценарии, в которых использование указателей является "хорошей идеей" (TM)?
Возможный дубликат:
Общее использование указателей?
Я все еще изучаю основы С++, но я уже достаточно знаю, чтобы делать полезные небольшие программы.
Я понимаю концепцию указателей и примеры, которые я вижу в учебниках, имеют смысл для меня. Однако, на практическом уровне и будучи (бывшим) разработчиком PHP, я еще не уверен, что буду использовать их в своих программах.
Фактически, до сих пор я не ощущал необходимости использовать какой-либо указатель. У меня есть свои классы и функции, и я, кажется, прекрасно разбираюсь, не используя никаких указателей (не говоря уже о указателях указателей). И я не могу не чувствовать себя немного гордясь своими маленькими программами.
Тем не менее, я знаю, что мне не хватает одной из наиболее важных функций С++, двухсторонняя: указатели и управление памятью могут создавать хаос, казалось бы, случайные сбои, трудно найти ошибки и дыры в безопасности... но на В то же время, правильно используемые, они должны обеспечивать умное и эффективное программирование.
Итак: скажите мне, что мне не хватает, не используя указатели.
Что такое хорошие сценарии, где использование указателей является обязательным?
Что они позволяют вам делать то, что вы не могли сделать иначе?
Каким образом они делают ваши программы более эффективными?
А как насчет указателей на указатели???
[Изменить: все различные ответы полезны. Одна из проблем в SO заключается в том, что мы не можем "принять" более одного ответа. Мне часто хотелось, чтобы я мог. На самом деле, все ответы были объединены, что помогает лучше понять всю картину. Спасибо.]
Ответы
Ответ 1
Указатели обычно используются в С++. Удовлетворяясь ими, вы сможете понять более широкий диапазон кода. Тем не менее, если вы можете избежать их, что отлично, однако, со временем, когда ваши программы станут более сложными, вам, скорее всего, понадобятся они, даже если они будут взаимодействовать с другими библиотеками.
-
В основном указатели используются для обозначения динамически распределенной памяти (возвращается new
).
-
Они позволяют функциям принимать аргументы, которые невозможно скопировать в стек либо потому, что они слишком большие или не могут быть скопированы, например объект, возвращаемый системным вызовом. (Я думаю, что выравнивание стека, может быть проблемой, но слишком туманно, чтобы быть уверенным.)
-
В встроенной программе они используются для обозначения таких вещей, как аппаратные регистры, которые требуют, чтобы код записывался в конкретный адрес в памяти.
-
Указатели также используются для доступа к объектам через их интерфейсы базового класса. То есть, если у меня есть класс B, который является производным от класса A class B : public A {}
. Это экземпляр объекта B может быть доступен, как если бы он был классом A, указав свой адрес указателю на класс A, то есть: A *a = &b_obj;
-
Это идиома C, чтобы использовать указатели как итераторы на массивах. Это может быть все еще распространено в старшем С++-коде, но, вероятно, считается плохим кузеном для объектов итератора STL.
-
Если вам нужно взаимодействовать с кодом C, вам будет необходимо обрабатывать указатели, которые используются для ссылки на динамически выделенные объекты, поскольку ссылок нет. Строки C - это просто указатели на массив символов, завершаемый символом nul '\ 0'.
Как только вы почувствуете себя комфортно с указателями, указатели на указатели не будут казаться такими ужасными. Наиболее очевидным примером является список аргументов для main()
. Обычно это объявляется как char *argv[]
, но я видел, что он объявлен (юридически я считаю) как char **argv
.
Объявление - это стиль C, но он говорит, что у меня есть массив указателей на указатели на char. Это интерпретируется как массив произвольного размера (размер переносится argc) строк стиля C (массивы символов, завершаемые символом nul '\ 0').
Ответ 2
Я использую указатели, когда хочу предоставить класс доступ к объекту, не давая ему права собственности на этот объект. Даже тогда я могу использовать ссылку, если мне не нужно изменить, к какому объекту я обращаюсь, и/или мне нужна опция без объекта, и в этом случае указатель будет NULL.
Ответ 3
Этот вопрос задан на SO перед. Мой ответ оттуда:
Я использую указатели примерно каждые шесть строк в коде С++, который я пишу. В верхней части моей головы это наиболее распространенное использование:
- Когда мне нужно динамически создавать объект, срок жизни которого превышает область, в которой он был создан.
- Когда мне нужно выделить объект, размер которого неизвестен во время компиляции.
- Когда мне нужно передать право собственности на объект из одной вещи в другую, не копируя ее (например, в связанном списке/куче/независимо от действительно больших дорогостоящих структур).
- Когда мне нужно обратиться к одному и тому же объекту из двух разных мест.
- Когда мне нужно нарезать массив без его копирования.
- Когда мне нужно использовать встроенные функции компилятора, чтобы генерировать инструкции, специфичные для процессора, или работать с ситуациями, когда компилятор испускает субоптимальный или наивный код.
- Когда мне нужно писать непосредственно в определенную область памяти (поскольку она имеет IO с отображением памяти).
Ответ 4
Если вы не почувствовали необходимости в указателях, я бы не стал тратить много времени на беспокойство о них, пока не возникнет необходимость.
Тем не менее, один из основных указателей пути может способствовать более эффективному программированию, избегая копий фактических данных. Например, предположим, что вы пишете сетевой стек. Вы получаете пакет Ethernet для обработки. Вы последовательно передаете эти данные в стек из "необработанного" Ethernet-драйвера в IP-драйвер в драйвер TCP, скажем, HTTP-драйвер, который обрабатывает HTML-код, который он содержит.
Если вы создаете новую копию содержимого для каждого из них, вы в конечном итоге делаете не менее четырех копий данных, прежде чем вы действительно обходите их вообще.
Использование указателей может избежать этого - вместо копирования самих данных вы просто обходите указатель на данные. Каждый последующий слой сетевого стека смотрит на свой собственный заголовок и передает указатель на то, что он считает "полезной нагрузкой" на следующий более высокий уровень в стеке. Этот следующий слой смотрит на свой собственный заголовок, изменяет указатель, чтобы показать, что он считает полезной нагрузкой, и передает его в стек. Вместо четырех копий данных все четыре слоя работают с одной копией реальных данных.
Ответ 5
Большое значение для указателей - это динамическое масштабирование массивов. Когда вы не знаете размер массива во время компиляции, вам нужно будет выделить его во время выполнения.
int *array = new int[dynamicSize];
Если ваше решение этой проблемы заключается в использовании std::vector
из STL, они используют динамическое выделение памяти за кулисами.
Ответ 6
Существует несколько сценариев, в которых требуются указатели:
- Если вы используете абстрактные базовые классы с помощью виртуальных методов. Вы можете провести std::vector и пропустить все эти объекты и вызвать виртуальный метод. Это ТРЕБУЕТ указателей.
- Вы можете передать указатель на буфер для чтения метода из файла и т.д.
- Вам нужно много памяти, выделенной в куче.
С самого начала нужно заботиться о проблемах с памятью. Поэтому, если вы начинаете использовать указатели, вы можете также взглянуть на интеллектуальные указатели, например boost shared_ptr.
Ответ 7
What are good scenarios where using pointers is a must?
Интервью. Внедрение strcpy.
What do they allow you to do that you couldn't do otherwise?
Использование иерархии наследования. Структуры данных, такие как двоичные деревья.
In which way to they make your programs more efficient?
Они дают больше контроля программисту, для создания и удаления ресурсов во время выполнения.
And what about pointers to pointers???
Часто задаваемый вопрос интервью. Как вы создадите двухмерный массив в куче.
Ответ 8
Указатель имеет специальное значение NULL
, эта ссылка не будет. Я использую указатели везде, где NULL
является допустимым и полезным значением.
Ответ 9
Я просто хочу сказать, что я редко использую указатели. Я использую ссылки и stl-объекты (deque, list, map и т.д.).
Хорошая идея - когда вам нужно вернуть объект, в котором должна быть освобождена функция вызова, или когда вы не хотите возвращать значение.
List<char*>* fileToList(char*filename) { //dont want to pass list by value
ClassName* DataToMyClass(DbConnectionOrSomeType& data) {
//alternatively you can do the below which doesnt require pointers
void DataToMyClass(DbConnectionOrSomeType& data, ClassName& myClass) {
Это в значительной степени единственная ситуация, которую я использую, но я не думаю, что это сложно. Также, если я хочу, чтобы функция изменяла переменную и не могла использовать возвращаемое значение (скажем, мне нужно больше одного)
bool SetToFiveIfPositive(int**v) {
Ответ 10
Вы можете использовать их для связанных списков, деревьев и т.д.
Это очень важные структуры данных.
Ответ 11
В общем, указатели полезны, поскольку они могут содержать адрес части памяти. Они особенно полезны в некоторых драйверах низкого уровня, где они эффективно используются для работы с байтом памяти байтом. Это самое мощное изобретение, которое С++ наследует от C.
Что касается указателя на указатель, вот пример "привет-мир", показывающий вам, как его использовать.
#include <iostream>
void main()
{
int i = 1;
int j = 2;
int *pInt = &i; // "pInt" points to "i"
std::cout<<*pInt<<std::endl; // prints: 1
*pInt = 6; // modify i, i = 6
std::cout<<i<<std::endl; // prints: 6
int **ppInt = &pInt; // "ppInt" points to "pInt"
std::cout<<**ppInt<<std::endl; // prints: 6
**ppInt = 8; // modify i, i = 8
std::cout<<i<<std::endl; // prints: 8
*ppInt = &j; // now pInt points to j
*pInt = 10; // modify j, j = 10
std::cout<<j<<std::endl; // prints: 10
}
Как мы видим, "pInt" является указателем на целое число, которое указывает на "i" в начале. С его помощью вы можете изменить "i". "ppInt" - указатель на указатель, который указывает на "pInt". С его помощью вы можете изменить "pInt", который является адресом. В результате "* ppInt = & j" теперь указывает на "pInt" на "j". Таким образом, мы имеем все вышеизложенные результаты.