Почему бы не использовать указатели для всего в C++?

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

Intent intent = new Intent(android.content.Intent.ACTION_VIEW, 
    Uri.parse("https://www.google.com/maps/dir/48.8276261,2.3350114/48.8476794,2.340595/48.8550395,2.300022/48.8417122,2.3028844"));
startActivity(intent);

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

Просто продолжайте добавлять путевые точки, указав в конце «/ широта, долгота». Существует, по-видимому, ограничение в 23 путевых точки согласно Google Docs . Не уверен, относится ли это и к Android.

75
задан Peter Mortensen 11 August 2010 в 19:05
поделиться

22 ответа

Да, один находится в стеке, а другой - в куче. Есть два важных отличия:

  • Первое, очевидное и менее важное: распределение кучи выполняется медленно. Распределение стека происходит быстро.
  • Вторым и гораздо более важным является RAII . Поскольку версия с выделенным стеком очищается автоматически, она полезна . Его деструктор вызывается автоматически, что позволяет гарантировать, что любые ресурсы, выделенные классом, будут очищены. Это очень важно, как избежать утечек памяти в C ++. Вы избегаете их, никогда не вызывая delete самостоятельно, вместо этого оборачивая его в объекты с выделенным стеком, которые вызывают delete внутри, обычно в их деструкторе. Если вы попытаетесь вручную отслеживать все распределения, и вызывайте delete в нужное время, я гарантирую, что у вас будет как минимум утечка памяти на 100 строк кода.

В качестве небольшого примера рассмотрим следующий код:

class Pixel {
public:
  Pixel(){ x=0; y=0;};
  int x;
  int y;
};

void foo() {
  Pixel* p = new Pixel();
  p->x = 2;
  p->y = 5;

  bar();

  delete p;
}

Довольно невинно код, правда? Мы создаем пиксель, затем вызываем какую-то несвязанную функцию, а затем удаляем пиксель. Есть ли утечка памяти?

И ответ «возможно». Что произойдет, если bar вызовет исключение? delete никогда не вызывается, пиксель никогда не удаляется, и происходит утечка памяти. Теперь рассмотрим следующее:

void foo() {
  Pixel p;
  p.x = 2;
  p.y = 5;

  bar();
}

Это не приведет к утечке памяти. Конечно, в этом простом случае все находится в стеке, поэтому он очищается автоматически, но даже если бы класс Pixel произвел внутреннее динамическое распределение, утечки не было бы. Классу Pixel просто будет дан деструктор, который его удаляет, и этот деструктор будет вызываться независимо от того, как мы выйдем из функции foo . Даже если мы оставим его, потому что bar сгенерировал исключение. Следующий, слегка надуманный пример показывает это:

class Pixel {
public:
  Pixel(){ x=new int(0); y=new int(0);};
  int* x;
  int* y;

  ~Pixel() {
    delete x;
    delete y;
  }
};

void foo() {
  Pixel p;
  *p.x = 2;
  *p.y = 5;

  bar();
}

Класс Pixel теперь внутренне выделяет некоторую память кучи, но его деструктор позаботится об ее очистке, поэтому, когда использует класс, нам не нужно беспокоиться об этом. (Я должен, вероятно, упомянуть, что последний пример здесь сильно упрощен, чтобы показать общий принцип. Если бы мы действительно использовали этот класс, он также содержал бы несколько возможных ошибок. Если распределение y не удается, x никогда не освобождается , и если Pixel копируется, мы заканчиваем тем, что оба экземпляра пытаются удалить одни и те же данные. Итак, возьмем последний пример с недоверием. Реальный код немного сложнее, но он показывает общую идею)

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

187
ответ дан 24 November 2019 в 11:24
поделиться

Первый вариант лучше всего, если в класс Pixel не добавлено больше членов. По мере того, как добавляется все больше и больше участников, существует вероятность исключения переполнения стека

-2
ответ дан 24 November 2019 в 11:24
поделиться

Объекты, созданные в стеке, создаются быстрее, чем выделенные объекты.

Почему?

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

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

Код проще, если вы не используете указатели. Если ваш дизайн позволяет вам использовать объекты стека, я рекомендую вам это сделать.

Я сам не стал бы усложнять проблему, используя интеллектуальные указатели.

OTOH Я немного поработал над встроенным полем и создал объекты на стек не очень умный (так как стек, выделенный для каждой задачи / потока, не очень большой - вы должны быть осторожны).

Так что это вопрос выбора и ограничений, нет ответа, чтобы вместить их все.

0
ответ дан 24 November 2019 в 11:24
поделиться

То, о чем я не упоминал, - это увеличенное использование памяти. Предполагая, что целые числа и указатели из 4 байтов

Pixel p;

будут использовать 8 байтов, а

Pixel* p = new Pixel();

будет использовать 12 байтов, увеличение на 50%. Это не кажется большим, пока вы не выделите достаточно для изображения 512x512. Тогда вы говорите 2 МБ вместо 3 МБ. Это игнорирует накладные расходы на управление кучей со всеми этими объектами на них.

0
ответ дан 24 November 2019 в 11:24
поделиться

The question is: why would you use pointers for everything? Stack allocated objects are not only safer and faster to create but there is even less typing and the code looks better.

1
ответ дан 24 November 2019 в 11:24
поделиться

Взгляд на вопрос под другим углом ...

В C ++ вы можете ссылаться на объекты, используя указатели ( Foo * ) и ссылки ( Foo & ). По возможности я использую ссылку вместо указателя. Например, при передаче по ссылке на функцию / метод использование ссылок позволяет коду (надеюсь) сделать следующие предположения:

  • Объект, на который имеется ссылка, не принадлежит функции / методу, поэтому не следует удалять ] предмет. Это все равно что сказать: «Вот, используйте эти данные, но верните их, когда закончите».
  • Ссылки на NULL-указатели менее вероятны. Можно передать ссылку NULL, но, по крайней мере, это не будет ошибкой функции / метода. Ссылку нельзя переназначить на новый адрес указателя, поэтому ваш код не мог случайно переназначить его на NULL или какой-либо другой недопустимый адрес указателя, что привело бы к ошибке страницы.
1
ответ дан 24 November 2019 в 11:24
поделиться

Используйте указатели и динамически размещаемые объекты ТОЛЬКО КОГДА ВЫ ДОЛЖНЫ. По возможности используйте статически выделенные (глобальные или стековые) объекты.

  • Статические объекты работают быстрее (без создания / удаления, без косвенного доступа к ним)
  • Нет времени жизни объекта, о котором стоит беспокоиться
  • Меньше нажатий клавиш Более читабельный
  • Намного более надежный. Каждый «->» - это потенциальный доступ к NIL или недействительной памяти

. Чтобы пояснить, под словом «статический» в данном контексте я подразумеваю нединамическое выделение. IOW, ничего НЕ в куче. Да, у них тоже могут быть проблемы со временем жизни объекта - с точки зрения порядка уничтожения синглтонов, - но размещение их в куче обычно ничего не решает.

2
ответ дан 24 November 2019 в 11:24
поделиться

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

Поскольку вы должны спросить себя: «Что произойдет, если этот параметр равен нулю?», Вы должны кодировать более осторожно, все время заботясь о нулевых проверках. Это говорит в пользу использования ссылок.

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

4
ответ дан 24 November 2019 в 11:24
поделиться

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

Забавно часть, конечно, заключается в том, что в каждом учебнике по Java, который я видел, упоминается, что сборщик мусора - это такая крутая штука, потому что вам не нужно помнить о вызове delete , когда на практике C ++ требует только delete при вызове new delete [] при вызове new [] ).

4
ответ дан 24 November 2019 в 11:24
поделиться

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

class Rectangle {
    Pixel top_left;
    Pixel bottom_right;
}

Rectangle r1; // Pixel is allocated on the stack
Rectangle *r2 = new Rectangle(); // Pixel is allocated on the heap

Основные преимущества переменных стека:

  • Вы можете использовать шаблон RAII для управления объектами. Как только объект выходит за пределы области видимости, вызывается его деструктор. Похоже на шаблон "using" в C #, но автоматически.
  • Нет возможности для нулевой ссылки.
  • Вам не нужно беспокоиться об управлении памятью объекта вручную.
  • Это приводит к меньшему выделению памяти. . Распределение памяти, особенно маленькое, вероятно, будет медленнее в C ++, чем в Java.

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

Однако

6
ответ дан 24 November 2019 в 11:24
поделиться

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

  {   // block of code that uses file
      File aFile("file.txt");
      ...
  }    // File destructor fires when file goes out of scope, closing the file
  aFile // can't access outside of scope (compiler error)

В случае создания файла вы должны не забыть удалить его, чтобы добиться того же поведения. В приведенном выше случае кажется простой проблемой. Однако рассмотрите более сложный код, например, для хранения указателей в структуре данных. Что, если вы передадите эту структуру данных другому фрагменту кода? Кто отвечает за уборку. Кто закроет все ваши файлы?

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

Эта концепция известна как RAII - выделение ресурсов - это инициализация, и она может значительно улучшить ваши возможности по приобретению и утилизации ресурсов.

6
ответ дан 24 November 2019 в 11:24
поделиться

Да, поначалу это имеет смысл, исходя из фона Java или C #. Не стоит забывать об освобождении выделенной памяти. Но затем, когда вы получите первую утечку памяти, вы будете чесать голову, потому что вы КЛЯЧЕТЕСЬ, что освободили все. Тогда во второй раз это произойдет, а в третий вы расстроитесь еще больше. Наконец, после шести месяцев головной боли из-за проблем с памятью вы начнете уставать от нее, и эта память, выделенная стеком, начнет выглядеть все более и более привлекательной. Как красиво и чисто - просто положи в стопку и забудь. Довольно скоро вы будете использовать стек в любое время, когда вам это сойдет с рук.

Но - ничто не заменит этот опыт. Мой совет? А пока попробуйте по-своему. Вы увидите.

10
ответ дан 24 November 2019 в 11:24
поделиться

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

Я бы рекомендовал изучить boost библиотека интеллектуальных указателей для ваших указателей.

6
ответ дан 24 November 2019 в 11:24
поделиться

Код:

Pixel p;
p.x = 2;
p.y = 5;

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

Использование new требует всех этих накладных расходов на управление памятью.

Тогда возникает вопрос - хотите ли вы использовать пространство стека или кучи для ваших данных. Переменные стека (или локальные), такие как 'p', не требуют разыменования, тогда как использование new добавляет уровень косвенности.

11
ответ дан 24 November 2019 в 11:24
поделиться

Хорошее общее практическое правило: НИКОГДА не используйте новые без крайней необходимости. Если вы не используете новую версию, вам будет проще поддерживать ваши программы, и они будут менее подвержены ошибкам, поскольку вам не придется беспокоиться о том, где их очистить.

11
ответ дан 24 November 2019 в 11:24
поделиться

«Почему бы не использовать указатели для всего в C ++»

Один простой ответ - потому что это становится огромной проблемой управления памятью - выделение и удаление / освобождение.

Автоматические объекты / объекты стека снимают часть загруженной работы.

это только первое, что я скажу по этому вопросу.

14
ответ дан 24 November 2019 в 11:24
поделиться

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

  • он быстрее
  • Мне не нужно беспокоиться об освобождении памяти
  • p будет действительным объект для всей текущей области видимости
24
ответ дан 24 November 2019 в 11:24
поделиться

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

Исходя из фона Java, вы можете не быть полностью готовы к тому, как много C ++ вращается вокруг отслеживания того, что был выделен и кто отвечает за его освобождение.

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

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

25
ответ дан 24 November 2019 в 11:24
поделиться

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

Итак, добавьте удаление. Убедитесь, что это происходит даже при распространении исключений.

Pixel* p = NULL; // Must do this. Otherwise new may throw and then
                 // you would be attempting to delete an invalid pointer.
try
{
    p = new Pixel(); 
    p->x = 2;
    p->y = 5;

    // Do Work
    delete p;
}
catch(...)
{
    delete p;
    throw;
}

Если вы выбрали что-то более интересное, например файл (который является ресурсом, который необходимо закрыть). Затем сделайте это правильно на Java с указателями, которые вам нужны.

File file;
try
{
    file = new File("Plop");
    // Do work with file.
}
finally
{
    try
    {
        file.close();     // Make sure the file handle is closed.
                          // Oherwise the resource will be leaked until
                          // eventual Garbage collection.
    }
    catch(Exception e) {};// Need the extra try catch to catch and discard
                          // Irrelevant exceptions. 

    // Note it is bad practice to allow exceptions to escape a finally block.
    // If they do and there is already an exception propagating you loose the
    // the original exception, which probably has more relevant information
    // about the problem.
}

Тот же код на C ++

std::fstream  file("Plop");
// Do work with file.

// Destructor automatically closes file and discards irrelevant exceptions.

Хотя люди упоминают скорость (из-за поиска / выделения памяти в куче). Лично для меня это не решающий фактор (распределители очень быстрые и оптимизированы для использования в C ++ небольших объектов, которые постоянно создаются / уничтожаются).

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

Основное различие между C ++ и Java:

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

Примеры:

 std::auto_ptr<Pixel>   p(new Pixel);
 // An auto_ptr has move semantics.
 // When you pass an auto_ptr to a method you are saying here take this. You own it.
 // Delete it when you are finished. If the receiver takes ownership it usually saves
 // it in another auto_ptr and the destructor does the actual dirty work of the delete.
 // If the receiver does not take ownership it is usually deleted.

 std::tr1::shared_ptr<Pixel> p(new Pixel); // aka boost::shared_ptr
 // A shared ptr has shared ownership.
 // This means it can have multiple owners each using the object simultaneously.
 // As each owner finished with it the shared_ptr decrements the ref count and 
 // when it reaches zero the objects is destroyed.

 boost::scoped_ptr<Pixel>  p(new Pixel);
 // Makes it act like a normal stack variable.
 // Ownership is not transferable.

Есть и другие.

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

Примеры:

 std::auto_ptr<Pixel>   p(new Pixel);
 // An auto_ptr has move semantics.
 // When you pass an auto_ptr to a method you are saying here take this. You own it.
 // Delete it when you are finished. If the receiver takes ownership it usually saves
 // it in another auto_ptr and the destructor does the actual dirty work of the delete.
 // If the receiver does not take ownership it is usually deleted.

 std::tr1::shared_ptr<Pixel> p(new Pixel); // aka boost::shared_ptr
 // A shared ptr has shared ownership.
 // This means it can have multiple owners each using the object simultaneously.
 // As each owner finished with it the shared_ptr decrements the ref count and 
 // when it reaches zero the objects is destroyed.

 boost::scoped_ptr<Pixel>  p(new Pixel);
 // Makes it act like a normal stack variable.
 // Ownership is not transferable.

Есть и другие.

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

Примеры:

 std::auto_ptr<Pixel>   p(new Pixel);
 // An auto_ptr has move semantics.
 // When you pass an auto_ptr to a method you are saying here take this. You own it.
 // Delete it when you are finished. If the receiver takes ownership it usually saves
 // it in another auto_ptr and the destructor does the actual dirty work of the delete.
 // If the receiver does not take ownership it is usually deleted.

 std::tr1::shared_ptr<Pixel> p(new Pixel); // aka boost::shared_ptr
 // A shared ptr has shared ownership.
 // This means it can have multiple owners each using the object simultaneously.
 // As each owner finished with it the shared_ptr decrements the ref count and 
 // when it reaches zero the objects is destroyed.

 boost::scoped_ptr<Pixel>  p(new Pixel);
 // Makes it act like a normal stack variable.
 // Ownership is not transferable.

Есть и другие.

30
ответ дан 24 November 2019 в 11:24
поделиться

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

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

4
ответ дан 24 November 2019 в 11:24
поделиться

Обычно, когда вы используете необработанные указатели, у вас нет RAII.

0
ответ дан 24 November 2019 в 11:24
поделиться

Почему бы не использовать указатели для всего?

Они медленнее.

Оптимизация компилятора будет не столь эффективной с симантикой доступа указателя, вы можете прочитать об этом в любом количестве веб-сайты, но вот достойный pdf от Intel.

Проверить страницы, 13,14,17,28,32,36;

Обнаружение ненужной памяти ссылки в обозначении цикла:

for (i = j + 1; i <= *n; ++i) { 
X(i) -= temp * AP(k); } 

Обозначение границ цикла содержит указатель или память ссылка. В компиляторе нет любые средства, чтобы предсказать, будет ли значение на который ссылается указатель n, изменено с итерациями цикла некоторыми другое задание. Это использует цикл чтобы перезагрузить значение, указанное в n для каждой итерации. Генератор кода двигатель также может отказать в планировании программный конвейерный цикл, когда потенциал обнаружен псевдоним указателя. Поскольку значение, на которое ссылается указатель n, не внутри цикла, и это инвариантно к индексу цикла, загрузка * нс для перевозки вне границ цикла для более простое планирование и указатель устранение неоднозначности.

... несколько вариаций на эту тему ....

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

2
ответ дан 24 November 2019 в 11:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: