Я пишу на Delphi, и раньше у меня было много проблем из-за багов с утечкой памяти (когда я в коде создал объект или динамический массив, а потом забыл его освободить). Теперь я решил эту проблему так: написал объект “сборщик мусора”, который запоминает все создаваемые программой классы и динамические массивы, и в конце работы подсчитывает, какие из них не были уничтожены, и выводит отчёт в текстовом файле. Выглядит код примерно так:
Код: Выделить всё
type
TSafeObject=class
public
constructor Create;
destructor Destroy; override;
procedure SetThisObjectAsDebugging;//Не будет выводиться сообщение об ошибке
end;
TGarbageFinder=class
private
FObjectsCount:integer;
FObjectsCapacity:integer;
FObjects:array of record
obj:tobject;
CreationTime:tdatetime;
CreationTimeTicks:longword;
Tag:integer;
PosInFullLog:integer;
FirstNum:integer;//Под каким номером был этот объект в самом начале
RandTag:integer;
Debugging:boolean;//Для таких не надо выдавать сообщение об ошибке
end;
FMemoryBlocks:array of record
MemMos:pointer;
BytesCount:integer;
CreationTime:tdatetime;
CreationTimeTicks:longword;
end;
FMemoryBlocksCount:integer;
FMemoryBlocksCapacity:integer;
FLogMessages:tstringlist;
FFullLogCount:integer;
FFullLogCapacity:integer;
FFullLog:array of record
LogMesType:integer;
PostTimeTicks:cardinal;
PostTime:tdatetime;
end;
FObjectsSaved:tlist;
FSaveRepFileName:tfilename;
FReport:tstringlist;
FSectionStarted:boolean;
FSectionsCount:integer;
FTwiceDestroyedObjectsCount:integer;
FTotalObjectsNotDestroyedCount:integer;
FTotalMemoryBlocksNotDestroyedCount:integer;
FTotalTwiceDestroyedObjectsCount:integer;
procedure Grow;
procedure GrowFullLog;
procedure GrowMemBlocksArray;
procedure DeleteObject(objnum:integer);
procedure DeleteMemoryBlock(blocknum:integer);
procedure AddObject(obj:tobject; crtime:tdatetime; crtimeticks:longword);
procedure AddMemoryBlock(pos:pointer; bytescount:integer);
function AddMessageToLogMessagesList(str:string):integer;//Возвращает индекс этой строки в FLogMessages;
procedure AddItemToFullLog(messtypenum:integer);
procedure SetObjectsCapacity(newcapacity:integer);
procedure SetFullLogCapacity(newcapacity:integer);
procedure SetMemoryBlocksCapacity(newcapacity:integer);
function NotDebuggingObjectsCount:integer;//Количество объектов за исключением debugging
public
FReportFileTooBig:boolean;
function SomeGarbageRemaining:boolean;
function FindObject(obj:tobject; direction:tdirection):integer;//-1 если нет
function FindMemoryBlock(pos:pointer; direction:tdirection):integer;
procedure SetObjectTag(obj:tsafeobject; newtag:integer);
procedure AddStringToFullLog(str:string);
procedure StartSection;
procedure FinishSection;
procedure AddToReport(str:string);
procedure ObjectCreated(obj:tobject); overload;
//procedure ObjectCreated(obj:tobject; logstr:string); overload;
procedure ObjectDestroyed(obj:tobject);
procedure MemoryBlockCreated(pos:pointer; bytescount:integer);
procedure MemoryBlockDestroyed(pos:pointer);
procedure PlaceTag(tag:integer);
constructor Create(repfilename:tfilename);
procedure FinalizeReport;
procedure WriteReportToFile;
destructor Destroy; override;
end;
var
ProjectGarbageFinder:TGarbageFinder;
…
constructor TSafeObject.Create;
begin
inherited;
if poGarbageFinderActive then begin
ProjecTGarbageFinder.ObjectCreated(self);
end;
end;
destructor TSafeObject.Destroy;
begin
if poGarbageFinderActive then begin
ProjecTGarbageFinder.ObjectDestroyed(self);
end;
inherited;
end;
Должен сказать, что такой подход у меня во многом от моего непрофессионализма – если бы я раньше знал про переменную ReportMemoryLeaksOnShutdown в Delphi, мне бы изначально, возможно, всё это писать не потребовалось бы. Хотя я думаю, что всё равно мой сборщик мусора полезен как дополнение к этой опции.
Как я понимаю, для C++ можно написать такую же штуку, а для более "продвинутых" языков вроде C# или Питона она не нужна, так как в них есть свой собственный сборщик мусора (но за это приходится платить скоростью работы программы).
Мне хотелось бы больше узнать про эти “высокоуровневые” языки. Хотелось бы почитать книги, где кратко изложена “философская суть” этих языков, их принципы. Вот здесь, например, кратко изложены принципы Python:
Кто знает Питон – согласны с этим текстом? Хотя я без примеров, конечно, пока ничего всё равно не понимаю.Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
У меня такая идея: придумать решения для Delphi и C++, которые смогут помогать реализовать эти же принципы. Это могут быть объекты вроде этого сборщика мусора, или программа, переписывающая мой собственный код, или программа, проверяющая код на определённые правила и выполняющая эвристический анализ багов. С правилами это будет выглядеть, наверно, так: программист принимает решение соблюдать определённые правила при написании кода, программа это проверяет, и проверяет ещё что-нибудь, и благодаря этому баги делаются гораздо реже.
Ещё раз, мой вопрос – правильно ли я понимаю, что эти высокоуровневые языки предлагают средства, с которыми совершаешь меньше ошибок, чем на старых языках; что это за средства, и нельзя ли их реализовать в рамках старых языков.
Может быть, то, о чём я думаю, давно реализовано — Pascal Analyser, Code Healer. Я сходу посмотрел сайт Code Healer, и поскольку не увидел галереи с достаточно удобным описанием сути фич программы, уже думаю что смогу сделать как минимум не хуже.