Completed Chapter 3 Exception and Error Handling, excerpt
Constructors and Exceptions
This topic is often mentioned in the C++ community, but no one seems to have paid attention to it in the Delphi community. Perhaps due to the characteristics of the language, Delphi programmers do not need to worry about this issue. But I think Delphi programmers should also understand this problem and know what the language provides us to make it so easy for us to ignore it. As the saying goes, "When you are surrounded by blessings, you must know the blessings." We know that the constructor of a class has no return value. If the constructor fails to construct the object, it is impossible to rely on returning an error code. So, how to identify the failure of the constructor in the program? The most "standard" method is: throw an exception. The failure of the constructor means that the construction of the object fails. So after an exception is thrown, how will this "half-dead" object be handled? Here, I think it is necessary to have an understanding of how C++ handles this situation before reading. In C++, the destructor is not called after the constructor throws an exception. This approach is reasonable because the object has not been completely constructed at this time. If the constructor has done some operations such as allocating memory, opening files, etc., then the C++ class needs to have its own members to remember what actions have been performed. Of course, this is very troublesome for class implementers, so generally C++ class implementers avoid throwing exceptions in constructors (you can provide a member function such as Init and UnInit, which is left to the constructor or class client). They are called to handle initialization failure). The solution provided by every classic C++ book is to use smart pointers (STL's standard class auto_ptr). In Object Pascal, this problem becomes very simple, and programmers don't have to worry about it. If an Object Pascal class throws an exception in the constructor, the compiler will automatically call the destructor of the class (since the destructor is not allowed to be overloaded, it is guaranteed that there is only one destructor, so the compiler will not be confused. among multiple destructors). Member objects are generally destructed in the destructor, and the Free() method ensures that the destructor will not be called on nil objects (that is, member objects that have not yet been created). Therefore, while making the code concise and beautiful, it also ensures Safety. type MyClass = classPRivateFStr : PChar; // String pointer publicconstructor Create();destructor Destroy(); override;end;constructor MyClass.Create();beginFStr := StrAlloc(10); // String pointer in the constructor Allocate memoryStrCopy(FStr, 'ABCDEFGHI');raise Exception.Create('error'); // Throwing exception, no reason, hahaend;destructor A.Destroy();beginStrDispose(FStr); // Release memory in destructor WriteLn('Free Resource');end;varObj : TMyClass;i : integer;begintryObj : = TMyClass.Create();Obj.Free();WriteLn('Succeeded');exceptObj := nil;WriteLn('Failed');end;Read(i); // Pause the screen to observe the running results end. In this code, the constructor throws an exception, and the execution result is: Free ResourceFailed at this time" Free Resource" output is produced by the compiler automatically calling the destructor. Therefore, if the class documentation or the class author tells you that the class constructor may throw an exception, then remember to wrap it with try...except! The different ways C++ and Object Pascal deal with exceptions thrown by constructors are actually the embodiment of the design ideas of the two languages. C++ adheres to the style of C and focuses on efficiency. Everything is left to the programmer and the compiler does not make unnecessary actions. Object Pascal inherits Pascal's style and focuses on the aesthetic meaning of the program. The compiler helps programmers complete complex work.