And to which extent is FPC different to C++ in terms of MemoryModel? (no sarcastic comment!)
Like what are there more of limitations which cant be just added, like I have hard times really understanding that objects are not capable of ineriting records, where da hell is the difference, like they can both be placed on stack, both can have functions/procs, both can contain pointer to other data, both cannot implement interfaces the ONLY difference is that object can inherit other objects thats it, for some reason, im sry @PascalDragon i cannott believe that they are soooooo much different in terms of implementation.. if this is the only thing they are different of.
There is one limitation that Pascal through the structure of the language has, that C++ simply does not have with regards to the memory model, and this is scoping.
Take this for example:
void Foo() {
// A lot of code
{
SomeObject obj;
// Some code
}
// A lot of code
}
obj's lifetime is restricted to the block it is located in, i.e. from the point of where it is defined, to the } of the block it is defined in. In pascal every variables lifetime is always the whole function it is defined in.
This has some non-trivial implications. C++ automatically calls the destructor, as well as, if no other constructor is explicetly used, also the constructor that takes no arguments. Meaning the destructor in C++ and the no argument constructor are comparable to the management operators in advanced records.
This allows for the following constructs:
void foo() {
{
std::ofstream file_stream("foo.txt");
file_stream << "foo\n";
}
// a lot of code
}
The constructor here opens the file foo.txt, and the destructor automatically closes it when the } is reached. This means the file is closed during the "a lot of code" section.
In pascal this would not be possible this way, because the lifetime of a variable ends with the end of the function, if the same mechanism would be used (i.e. management operators), the file would be kept open during all of the other code section.
That of course gives C++ in that regard a lot more control over the lifetime of objects. Another thing is the initialization/constructor:
if (condition) {
SomeObj obj;
//...
}
In C++ obj would only be initialized if the condition is true. In pascal the object must be initialized when the function starts and finalized when the function returns.
This has some implications. 1. through the plaicing of blocks you can explicetly define when and where the objects initialization and finalization code is gonna be called and 2. you don't need try-finally anymore (in fact try-finally does not exist in C++) as the destructor is called like a finally block.
If you want the same level of control in pascal you can not use management operators, but must resort to manual constructor and destructor calling, like it is done with old style objects:
procedure Foo();
var
file_stream: OStream; // just pretend there is an object or record like this
begin
// A lot of code
file_stream.init('foo.txt');
try
file_stream.WriteLn('foo');
finally
file_stream.close;
end;
// A lot of code
end;
And this is the strength of C++ classes and it's memory model. You can archive the same level of control over the lifetime with much less code. Because at this point, where you manually have to call the constructor and destructor, the only advantage a stack object has over a heap based class, is a tiny bit of performance.
Personally I think in most situations clean code is more important than performance. And while C++ has a lot of things that make code really hard to read and understand, it's scope based lifetime of objects is a massive advantage to keeping your code clean. And this is something that is just by the language design never possible in pascal.
That said, often enough I think having objects live to long, or being initialized even if it is not necessary is only a minor drawback in performance. And if the performance does not matter, management operators allow for much cleaner code, as their C++ equivalent. You just loose some lifetime control and performance. But I would argue that most of the time this does not matter.
And in fact, I already build a file stream record type like the C++ example above using management operators. By putting all the file related stuff in it's own function, you still guarantee the file is not open unessecarily long, while simultaniously getting rid of the try-finally block and the manual calling of the destructor, massively cleaning up the code:
procedure CopyFile(const source, target: String);
var
fr: TFileReader;
fw: TFileWriter;
line: string;
begin
fr.Open(source);
fl.Open(target);
for line in fr.lines do
fl.WriteLn(line);
end;
And this is why I love management operators (just an example, I actually implemented a more efficient copyfile function that does not work on a line basis but on a fixed size buffer basis)
About the internal implementation of records and objects. You must see it in a different way, advanced records are, with respect to the lifetime of the pascal language, fairly new. First there were records, then there were objects, then classes and then advanced records.
So while today the functionality of objects and advanced records might be quite similar, they developed completely differently.
I don't know how they are implemented in the FPC, but I can fully imagine that, as at first there was no intention to add features to records, that records and objects are completely differently implemented, and if you start from a different code base, it is going to develop differently in the future.
Also, what you should not forget, there are 2 different features, records can be variant, i.e. have different fields sharing the same memory:
TColorRec = record
case Boolean of
True: (color: TColor);
False: (R, G, B, A: Byte);
end;
...
colRec: TColorRec;
...
colRec.R := 255;
colRec.G := 255;
colRec.B := 255;
colRec.A := 0;
Form1.Color := colRec.color
and a "unique" feature (compared to records) of objects is the usage of a virtual method table. So they are inherently different. So honestly, I don't have any doubt when PascalDragon says they are implemented differently that they are. They where historically completely different and it took a long time before they became so similar they are today.
For example, if I remember correctly, even though advanced records where already a thing, it took a while before records got constructors and with them the new(obj, constructor) syntax we know from objects. Originally they were never intendet to be so similar