Original in Russian: http://programmingmindstream.blogspot.ru/2014/09/blog-post_26.html
Depression – has “partly” vanished.
Lots of errors have been found.
Particularly – THANKS to NameRec - http://programmingmindstream.blogspot.ru/2014/09/blog-post_23.html?showComment=1411514362019#c1699121675114974966 (in Russian)
Load tests based on GUI-testing have been written.
They emulate the work of real-life users in the distributed system.
On a number of computers.
According to the order:
1. Open a document (out of the predefined list) randomly.
2. Insert “two or three words”.
3. Save it.
4. Close the document.
By the way, the test code:
My tests have been working for three days now.
About 9 Gbs of data has been processed.
I left it for a week-end. We’ll see what we’ll have.
I’ll write a profound analysis of results.
I can tell one thing now – even if function ReadFile has “read something” and returned ReadSize = SizeToRead – “that is not the reason to calm down”. Even if it “read” what it was intended to. Even if the results coincide with the file data.
One should check Result of ReadFile function, the BOOL. Finally, GetLastError.
For example, it can return LockViolation or NetworkBusy.
Trivial. Yeah.
But I’ll write about it “some time later”.
Now I’d like to write about constructors.
Based on:
Link. Getting of resource is initialization (RAII). Some of “my own ideas” (in Russian)
Briefly. Once again about factories
Briefly. Some more “reasoning about RAII”
Briefly. Again about factories
Briefly. About factories
My own implementation of IUnknown and reference counting. And mixins
Why we always need to use FreeAndNil instead of Free (in Russian) – this one worth a particular and attentive reading, since my thought comes from “the same paradigm”. Virtuality. And descendant classes.
Briefly. For those who don’t like FreeAndNil (in Russian)
Today I’ve got a five-time evidence of the fact that we have to write FreeAndNil instead of Free (in Russian)
Rule 9: Never call virtual functions in constructor or destructor (in Russian)
Virtual functions in constructor or destructor(in Russian)
Gunsmoker wrote about destructors and I want to write about constructors.
And so, here I come.
Usually constructors are written in this way:
This “style” is left to us by Borland who deceased in bose.
We have “become used” to it.
What is wrong with it?
Actually, it is “more correct” to write like this:
What have we done here?
We have SWAPPED “aggregated objects” initialization and “the call of inherited constructors”.
This is IMPORTANT.
Why?
The key word is - virtuality.
What do I mean?
For now let’s write in this way:
What is to be done?
Now let’s write in this way:
The problem has gone away.
Is the idea clear? http://programmingmindstream.blogspot.ru/2014/08/blog-post_64.html (in Russian)
I’ll note – “do not tell me about C++ and other languages”. They are organized in a different way http://ptgmedia.pearsoncmg.com/images/9780321334879/samplepages/0321334876.pdf (look at item 9, page 48).
Let me cite:
“It’s not a secret but a simple rule: virtual function is not virtual if it is called from constructor or destructor.
Rules should be learned by heart, which is inconvenient. To undersign the principle is more simple. In this case, the principle is in a cornerstone of inheritance implementation in C++: when creating the object, constructors in the hierarchy are called from base class up to the last inherited one. For destructors it is quite the reverse.
What do we get: class constructor always works with an assumption that descendant classes has not been yet created and, therefore, is has no right to call functions they define. As for virtual function, it has no choice but to call what it itself defines. It turns out that the virtual functions mechanism kind of does not work here. But it actually does not work since the table of descendant class virtual functions has not yet covered the current table.
It is quite the opposite for the destructor. Destructor “knows” that when it is called all the descendant classes have already been destructed and there’s nothing to call from them. For this reason it replaces the address of virtual functions table for its own table address and successfully calls virtual function version, which it itself defines.
So, virtual function is not virtual if it is called from constructor or destructor.”
Virtual functions in constructor or destructor (in Russian)
That’s speaking about C++ and “other languages”.
BUT!
That is not all about Delphi.
Now, “pair of words” on “where this Borland’s style comes from”.
It “comes” from Turbo Pascal and Turbo Vision.
There constructors Init and the base object TObject were.
However, it had entirely DIFFERENT PARADIGM. I repeat – the PARADIGM. I use Caps Lock for a reason.
Once again I repeat – DIFFERENT PARADIGM.
What was there?
This:
Of course, InstanceSize – “looked differently”.
But I believe “the idea is clear”.
What happens with the descendant object?
We’ll write in this way:
What about a “different paradigm”?
Is the idea clear? http://programmingmindstream.blogspot.ru/2014/08/blog-post_64.html (in Russian)
“Many people” still “keep thinking” of this PARADIGM. At the same time, they somehow “forget” to call constructor from TObject.
I’ve seen such thing in A NUMBER of third-party libraries.
For example, in ImageEn.
The “discussion” about constructors/destructors, factories (in Russian) and"RAII" – is not fully closed. It has just been opened.
Would you be interested, I’ll follow on it.
But “so far” that is “all I wanted to tell about constructors”.
About “where it came from” - Briefly. About triggering the exceptions
P.S. That is all due to the “one-eyed” architecture” (in russian), but provides good “motivation for a special talk”.
P.P.S. Actually, the idea за the post was TRIVIAL - either “using of the factories” or “shattering of paradigm” . http://programmingmindstream.blogspot.ru/2014/09/blog-post_26.html?showComment=1412024065216#c6012843482459411429 (in Russian)
Although, “for some” simple “using factories” is already “shattering the paradigm”. Unfortunately.
Depression – has “partly” vanished.
Lots of errors have been found.
Particularly – THANKS to NameRec - http://programmingmindstream.blogspot.ru/2014/09/blog-post_23.html?showComment=1411514362019#c1699121675114974966 (in Russian)
Load tests based on GUI-testing have been written.
They emulate the work of real-life users in the distributed system.
On a number of computers.
According to the order:
1. Open a document (out of the predefined list) randomly.
2. Insert “two or three words”.
3. Save it.
4. Close the document.
By the way, the test code:
USES QuickTestsUtils.script ; Test TK565491886 ARRAY VAR "Documents list" [ Document name ] >>> "Documents list" BOOLEAN VAR Eternity TRUE >>> Eternity WHILE Eternity ( INTEGER VAR "Random document" ( "Documents list" array:Count Random "Documents list" [i] ) >>> " Random document" Parameters: ( "Document from base {("Random document")}" ) Execute ( "Input the text {('We change the text and save it!!!')}" "Press Enter" // - in order to separate documents paragraphs, otherwise they be perceived as “too long” "Save the document" "Process the messages" // - to redraw the application ) ) ; TK565491886
My tests have been working for three days now.
About 9 Gbs of data has been processed.
I left it for a week-end. We’ll see what we’ll have.
I’ll write a profound analysis of results.
I can tell one thing now – even if function ReadFile has “read something” and returned ReadSize = SizeToRead – “that is not the reason to calm down”. Even if it “read” what it was intended to. Even if the results coincide with the file data.
One should check Result of ReadFile function, the BOOL. Finally, GetLastError.
For example, it can return LockViolation or NetworkBusy.
Trivial. Yeah.
But I’ll write about it “some time later”.
Now I’d like to write about constructors.
Based on:
Link. Getting of resource is initialization (RAII). Some of “my own ideas” (in Russian)
Briefly. Once again about factories
Briefly. Some more “reasoning about RAII”
Briefly. Again about factories
Briefly. About factories
My own implementation of IUnknown and reference counting. And mixins
Why we always need to use FreeAndNil instead of Free (in Russian) – this one worth a particular and attentive reading, since my thought comes from “the same paradigm”. Virtuality. And descendant classes.
Briefly. For those who don’t like FreeAndNil (in Russian)
Today I’ve got a five-time evidence of the fact that we have to write FreeAndNil instead of Free (in Russian)
Rule 9: Never call virtual functions in constructor or destructor (in Russian)
Virtual functions in constructor or destructor(in Russian)
Gunsmoker wrote about destructors and I want to write about constructors.
And so, here I come.
Usually constructors are written in this way:
type TObject1 = class end;//TObject1 TObject2 = class end;//TObject2 TA = class private f_SomeObject1 : TObject1; public constructor Create; end;//TA TB = class(TA) private f_SomeObject2 : TObject2; public constructor Create; end;//TB ... constructor TA.Create; begin inherited Create; f_SomeObject1 := TObject1.Create; end; ... constructor TB.Create; begin inherited Create; f_SomeObject2 := TObject2.Create; end;
This “style” is left to us by Borland who deceased in bose.
We have “become used” to it.
What is wrong with it?
Actually, it is “more correct” to write like this:
type TObject1 = class end;//TObject1 TObject2 = class end;//TObject2 TA = class private f_SomeObject1 : TObject1; public constructor Create; end;//TA TB = class(TA) private f_SomeObject2 : TObject2; public constructor Create; end;//TB ... constructor TA.Create; begin f_SomeObject1 := TObject1.Create; inherited Create; end; ... constructor TB.Create; begin f_SomeObject2 := TObject2.Create; inherited Create; end;
What have we done here?
We have SWAPPED “aggregated objects” initialization and “the call of inherited constructors”.
This is IMPORTANT.
Why?
The key word is - virtuality.
What do I mean?
For now let’s write in this way:
type TObject1 = class public SomeField : SomeDataType; end;//TObject1 TObject2 = class public SomeField : SomeDataType; end;//TObject2 TA = class private f_SomeObject1 : TObject1; f_SomeData : Integer; protected function CalcSomeData: Integer; virtual; public constructor Create; end;//TA TB = class(TA) private f_SomeObject2 : TObject2; protected function CalcSomeData: Integer; override; public constructor Create; end;//TB ... constructor TA.Create; begin inherited Create; f_SomeObject1 := TObject1.Create; f_SomeData := CalcSomeData; end; function TA.CalcSomeData: Integer; begin Result := SomeAlgorythm1(f_SomeObject1.SomeField); // - everything is OK here end; ... constructor TB.Create; begin inherited Create; f_SomeObject2 := TObject2.Create; end; function TB.CalcSomeData: Integer; begin Result := SomeAlgorythm2(f_SomeObject2.SomeField); // - we get AV, because on call of CalcSomeData from constructor TA.Create the field f_SomeObject2 is not initialized end;
What is to be done?
Now let’s write in this way:
type TObject1 = class public SomeField : SomeDataType; end;//TObject1 TObject2 = class public SomeField : SomeDataType; end;//TObject2 TA = class private f_SomeObject1 : TObject1; f_SomeData : Integer; protected function CalcSomeData: Integer; virtual; public constructor Create; end;//TA TB = class(TA) private f_SomeObject2 : TObject2; protected function CalcSomeData: Integer; override; public constructor Create; end;//TB ... constructor TA.Create; begin f_SomeObject1 := TObject1.Create; inherited Create; f_SomeData := CalcSomeData; end; function TA.CalcSomeData: Integer; begin Result := SomeAlgorythm1(f_SomeObject1.SomeField); // - everything is OK here end; ... constructor TB.Create; begin f_SomeObject2 := TObject2.Create; inherited Create; end; function TB.CalcSomeData: Integer; begin Result := SomeAlgorythm2(f_SomeObject2.SomeField); // - everything is OK here because field f_SomeObject2 is initialized due to a MORE LATE call of constructor end;
The problem has gone away.
Is the idea clear? http://programmingmindstream.blogspot.ru/2014/08/blog-post_64.html (in Russian)
I’ll note – “do not tell me about C++ and other languages”. They are organized in a different way http://ptgmedia.pearsoncmg.com/images/9780321334879/samplepages/0321334876.pdf (look at item 9, page 48).
Let me cite:
“It’s not a secret but a simple rule: virtual function is not virtual if it is called from constructor or destructor.
Rules should be learned by heart, which is inconvenient. To undersign the principle is more simple. In this case, the principle is in a cornerstone of inheritance implementation in C++: when creating the object, constructors in the hierarchy are called from base class up to the last inherited one. For destructors it is quite the reverse.
What do we get: class constructor always works with an assumption that descendant classes has not been yet created and, therefore, is has no right to call functions they define. As for virtual function, it has no choice but to call what it itself defines. It turns out that the virtual functions mechanism kind of does not work here. But it actually does not work since the table of descendant class virtual functions has not yet covered the current table.
It is quite the opposite for the destructor. Destructor “knows” that when it is called all the descendant classes have already been destructed and there’s nothing to call from them. For this reason it replaces the address of virtual functions table for its own table address and successfully calls virtual function version, which it itself defines.
So, virtual function is not virtual if it is called from constructor or destructor.”
Virtual functions in constructor or destructor (in Russian)
That’s speaking about C++ and “other languages”.
BUT!
That is not all about Delphi.
Now, “pair of words” on “where this Borland’s style comes from”.
It “comes” from Turbo Pascal and Turbo Vision.
There constructors Init and the base object TObject were.
However, it had entirely DIFFERENT PARADIGM. I repeat – the PARADIGM. I use Caps Lock for a reason.
Once again I repeat – DIFFERENT PARADIGM.
What was there?
This:
constructor TObject.Init; begin FillChar(@Self, InstanceSize, 0); end;
Of course, InstanceSize – “looked differently”.
But I believe “the idea is clear”.
What happens with the descendant object?
We’ll write in this way:
type TA = object(TObject) public SomeField : Integer; public constructor Init; end;//TA ... constructor TA.Init; begin TObject.Init; SomeField := 123; end; ... var A : TA; begin A := TA.Init; WriteLn(A.SomeField); // - here we get 123 end;
What about a “different paradigm”?
type TA = object(TObject) public SomeField : Integer; public constructor Init; end;//TA ... constructor TA.Init; begin SomeField := 123; TObject.Init; end; ... var A : TA; begin A := TA.Init; WriteLn(A.SomeField); // - here we get 0 end;
Is the idea clear? http://programmingmindstream.blogspot.ru/2014/08/blog-post_64.html (in Russian)
“Many people” still “keep thinking” of this PARADIGM. At the same time, they somehow “forget” to call constructor from TObject.
I’ve seen such thing in A NUMBER of third-party libraries.
For example, in ImageEn.
The “discussion” about constructors/destructors, factories (in Russian) and"RAII" – is not fully closed. It has just been opened.
Would you be interested, I’ll follow on it.
But “so far” that is “all I wanted to tell about constructors”.
About “where it came from” - Briefly. About triggering the exceptions
P.S. That is all due to the “one-eyed” architecture” (in russian), but provides good “motivation for a special talk”.
P.P.S. Actually, the idea за the post was TRIVIAL - either “using of the factories” or “shattering of paradigm” . http://programmingmindstream.blogspot.ru/2014/09/blog-post_26.html?showComment=1412024065216#c6012843482459411429 (in Russian)
Although, “for some” simple “using factories” is already “shattering the paradigm”. Unfortunately.
Комментариев нет:
Отправить комментарий