Другой возможный путь может состоять в том, чтобы создать два интерфейса для Доступного для поиска объекта, например, стога сена и объекта ToBeSearched, например, иглы. Так, это может быть сделано таким образом
public Interface IToBeSearched
{}
public Interface ISearchable
{
public void Find(IToBeSearched a);
}
Class Needle Implements IToBeSearched
{}
Class Haystack Implements ISearchable
{
public void Find(IToBeSearched needle)
{
//Here goes the original coding of find function
}
}
Перераспределение вещей, которые вам нужны
Не высвобождение вещей, которые вам больше не нужны (потому что вы плохо отслеживаете выделение / использование / освобождение)
Перераспределение новых экземпляров вещей, которые уже существуют (побочный эффект неправильного отслеживания)
Удаление того, что вы уже освободили
Удаление того, что не существует (нулевой указатель)
Вероятно, есть еще кое-что. Дело в том, что управлять памятью сложно, и с ней лучше всего справиться с помощью какого-то механизма отслеживания и выделения / освобождения абстракции. Таким образом, вы могли бы встроить это в свой язык, чтобы сделать его приятным и легким для вас. Ручное управление памятью - это не конец света - это, безусловно, выполнимо, - но в наши дни, если вы не пишете код в реальном времени, драйверы оборудования или (может быть,
В дополнение к сказанному шелковистым, вы также можете дважды освободить что-то.
Well, the errors you can make are:
There are other errors you can make, but those are the ones that relate specifically to garbage collection.
Помимо других комментариев, ручное управление памятью затрудняет выполнение некоторых высокопроизводительных параллельных алгоритмов.
Некоторые языки, отличные от GC, предлагают конструкции, называемые интеллектуальными указателями с подсчетом ссылок. Они пытаются обойти некоторые проблемы, например, забыть освободить память или попытаться получить доступ к недействительной памяти путем автоматизации некоторых функций управления.
Как некоторые говорили, вы должны быть «умными» в отношении «умных указателей». Интеллектуальные указатели помогают избежать целого класса проблем, но создают свой собственный класс проблем.
Многие интеллектуальные указатели могут создавать утечки памяти посредством:
Эти проблемы не должны можно встретить в средах с полностью GC.
В C вы должны вручную вызвать free
в памяти, выделенной с помощью malloc
. Хотя это звучит не так уж плохо, это может вызвать очень беспорядок при работе с отдельными структурами данных (например, связанными списками), которые указывают на одни и те же данные. Вы можете получить доступ к освобожденной памяти или двойному освобождению памяти, что вызывает ошибки и может привести к уязвимостям системы безопасности.
Кроме того, в C ++ нужно быть осторожным с смешиванием new [] / delete
и new / delete []
.
Например, для управления памятью программист должен точно знать, почему
const char *getstr() { return "Hello, world!" }
хорошо, а
const char *getstr() {
char x[BUF_SIZE];
fgets(x, BUF_SIZE, stdin);
return x;
}
- очень плохо.
Другой распространенной ошибкой является чтение или запись памяти после того, как вы ее освободили (память, которая с тех пор была перераспределена и теперь используется для чего-то еще, или память, которая еще не была повторно выделена и который, следовательно, в настоящее время все еще принадлежит диспетчеру кучи, а не вашему приложению).
Обычно языки со сборкой мусора ограничивают доступ программиста к памяти и полагаются на модель памяти, в которой объекты содержат :
По сравнению с языком, отличным от GC. , существует два класса ошибок, которые сокращаются / устраняются моделью и ограниченным доступом:
Ошибки модели памяти, такие как:
Ошибки указателя, такие как:
Есть и другие, но они большие ед.
Пожалуйста, не сравнивайте объектно-ориентированные языки (Java, C #) с не объектно-ориентированными языками (C), когда речь идет о сборке мусора. ОО-языки (в основном) позволяют реализовать сборщик мусора (см. Комментарий об интеллектуальных указателях). Да, они непростые, но они очень помогают, и они детерминированы.
Кроме того, как языки GC сравниваются с языками, отличными от GC, при рассмотрении ресурсов, отличных от памяти, например? файлы, сетевые соединения, соединения с БД и т. д.
Я думаю, что ответ на этот вопрос, оставленный читателю, также прольет некоторый свет на вещи.
IMO, garbage collected languages have complementary problems to those in non-garbage-collected languages. For every issue, there is a non-GC-characteristic bug and a GC-characteristic bug - a non-GC programmer responsibility and a GC programmer responsibility.
GC programmers may believe that they are relieved of responsibility for freeing objects, but objects hold resources other than memory - resources that often need to be released in a timely way so that they can be acquired elsewhere - e.g. file handles, record locks, mutexes...
Where a non-GC programmer would have a dangling reference (and very often one that isn't a bug, since some flag or other state would mark it as not to be used), a GC programmer has a memory leak. Thus where the non-GC programmer is responsible for ensuring that free/delete is called appropriately, a GC programmer is responsible for ensuring that unwanted references are nulled or otherwise disposed of appropriately.
There is a claim in here that smart pointers don't deal with garbage cycles. This need not be true - there are reference counting schemes that can break cycles and which also ensure timely disposal of garbage memory, and at least one Java implementation used (and may still do) a reference counting scheme that could just as easily be implemented as a smart pointer scheme in C++.
Concurrent Cycle Collection in Reference Counted Systems
Of course this isn't normally done - partly because you may as well just use a GC language, but also partly IMO because it would break key conventions in C++. You see, lots of C++ code - including the standard library - relies heavily on the Resource Allocation Is Initialisation (RAII) convention, and that relies on reliable and timely destructor calls. In any GC that copes with cycles, you simply cannot have that. When breaking a garbage cycle, you cannot know which destructor to call first without any dependency issues - it may not even be possible, since there may be more cyclic dependencies than just memory references. The solution - in Java etc, there is no guarantee that finalizers will be called. Garbage collection only collects one very specific kind of garbage - memory. All other resources must be cleaned up manually, as they would have been in Pascal or C, and without the advantage of reliable C++-style destructors.
End result - a lot of cleanup that gets "automated" in C++ has to be done manually in Java, C# etc. Of course "automated" needs the quotes because the programmer is responsible for ensuring that delete is called appropriately for any heap-allocated objects - but then in GC languages, there are different but complementary programmer responsibilities. Either way, if the programmer fails to handle those responsibilities correctly, you get bugs.
[EDIT - there are cases where Java, C# etc obviously do reliable (if not necessarily timely) cleanup, and files are an example of this. These are objects where reference cycles cannot happen - either because (1) they don't contain references at all, (2) there's some static proof that the references it contains cannot directly or indirectly lead back to another object of the same type, or (3) the run-time logic ensures that while chains/trees/whatever may be possible cycles are not. Cases (1) and (2) are extremely common for resource-managing objects as opposed to data-structure nodes - perhaps universal. The compiler itself cannot reasonably guarantee (3), though. So while standard library developers, who write the most important resource classes, can ensure reliable cleanup for those, the general rule is still that reliable cleanup of non-memory resources cannot be guaranteed for a GC, and this could affect application-defined resources.]
Frankly, switching from non-GC to GC (or visa versa) is no magic wand. It may make the usual suspect problems go away, but that just means you need new skillsets to prevent (and debug) an whole new set of suspects.
A good programmer should get past the whos-side-are-you-on BS and learn to handle both.