📄 chapter 11 constructors and destructors.htm
字号:
; call default constructor for x
; do something ...
; evaluate some conditional expression here ...
JZ __forward_001 ; jump if the condition was false
LOAD ERROR_CASE ; retval = ERROR_CASE
JMP __return_exit
__forward_001:
; do something else ...
; evaluate some other conditional expression here ...
JZ __forward_002 ; jump if the condition was false
; do some special case
LOAD SPECIAL_CASE ; retval = SPECIAL_CASE
STORE retval
JMP __return_exit
__forward_002:
LOAD GENERAL_CASE ; retval = GENERAL_CASE
STORE retval
__return_exit:
; call destructor of x
RTN
</PRE>
<P>This is a pretty accurate picture of what the SAL compiler does to insure
that all destructors get called. Notice that we do not really need a temporary
variable to store the return value. We can get away with pushing it onto the EES
and leaving it there. It should be the topmost item when the <TT>RTN</TT> gets
executed.</P>
<P><!-------------------------------------------------------------------------------->
<H2>11.6 Implementing Constructors and Destructors</H2><!-------------------------------------------------------------------------------->
<H3>11.6.1 Parsing Constructors and Destructors</H3><!-------------------------------------------------------------------------------->Again,
the parsing for constructors is straightforward. We will modify
<TT>RuleProcFuncBlock()</TT> in order to achieve the necessary functionality of
constructors and destructors.
<MENU><IMG src="Chapter 11 Constructors and Destructors.files/RULE46P.gif">
<P><FONT face=arial size=-1><B>Figure {RULE46P}</B> </FONT></P></MENU>
<MENU><IMG src="Chapter 11 Constructors and Destructors.files/RULE54P.gif">
<P><FONT face=arial size=-1><B>Figure {RULE54P}</B> </FONT></P></MENU>
<MENU><IMG src="Chapter 11 Constructors and Destructors.files/RULE55P.gif">
<P><FONT face=arial size=-1><B>Figure {RULE55P}</B> </FONT></P></MENU>
<MENU><IMG src="Chapter 11 Constructors and Destructors.files/RULE60P.gif">
<P><FONT face=arial size=-1><B>Figure {RULE60P}</B> </FONT></P></MENU>
<H3>11.6.2 Symbol Tables for Constructors and Destructors</H3><!-------------------------------------------------------------------------------->The
first modification that we need is to <TT>RuleClassType</TT>. We need to make
sure that our class has default constructors and destructors if they are needed.
This checklist will help us decide if we need to generate a default
constuctor/destructor. If the answer to any of these questions is no, then we do
not generate a default constuctor/destructor. Although we do not mention
destructors in the next few paragraphs, all of these rules apply equally and
without modification.
<OL>
<LI>Does our class have no constructors?
<LI>Does the class have any base classes?
<LI>Do any of the base classes have at least one constructor?
<LI>For all base classes with constructors, do all of them have a default
constructor (may be in addition to an arbitrary amount of non-default
constructors)? </LI></OL>If the answer to the last question is yes, then we
can/must generate a default constructor. If the answer was no, then we can do
nothing, and generate an error. The current class needs a constructor of some
sort, and the compiler has no way to generate a default constructor that can
call one of the specialized constructors for its base class. By design, a
non-default constructor was meant to initialize the class in a special, unique
way. If the class has a non-default constructor and no default constructor, then
it is impossible for the compiler to know how the class ought to be initialized
under general circumstances.
<P>In order to perform the above test, we can make use of two Boolean variables.
One is called <TT>hasCtor</TT>, and the other is called <TT>needsCtor</TT>. Both
are initialized to <TT>false</TT>. If our class declaration has any constructor,
then we set <TT>hasCtor</TT> to <TT>true</TT>. If any of our base classes has a
constructor, then we set <TT>needsCtor</TT> to <TT>true</TT>. This procedure
takes care of the first three questions. At the end of the class declaration we
can check these two variables and decide whether or not we need to generate a
default constructor.
<MENU>
<TABLE border=1>
<TBODY>
<TR>
<TH> </TH>
<TH>hasCtor = <TT>true</TT></TH>
<TH>hasCtor = <TT>false</TT></TH></TR>
<TR>
<TH>needsCtor = <TT>true</TT></TH>
<TD>Ok. Do nothing</TD>
<TD>Generate default constructor</TD></TR>
<TR>
<TH>needsCtor = <TT>false</TT></TH>
<TD>Ok. Do nothing</TD>
<TD>Ok. Do nothing</TD></TR></TBODY></TABLE></MENU>By now, the burning question
should be, "What about the fourth point"? If there exists a base class with no
default constructor and at least one non-default constructor, then we should
signal an error. It turns out that we can (and should) take care of this point
elsewhere when we process the super statement. This is a more general approach,
that also takes care of any type of constructor. Again, all of this behavior
applies equally to destructors. We need to make one additional modification to
<TT>RuleClassType()</TT> for the benefit of destructors. We need to add code for
the meta-destructor. Since the code for the meta-destructor and the class
destructor is one and the same, it is appropriate to mention it at this time.
<P>Figures {RULE12S} and {RULE13S} shows all of the appropriate modifications
that we need to make.
<P>
<MENU><IMG src="Chapter 11 Constructors and Destructors.files/RULE12S.gif">
<P><FONT face=arial size=-1><B>Figure {RULE12S}</B> The value, NO_CTOR_NEEDED
is a special constant that is used to signal to the compiler that no
constructor/destructor is needed for a class. The information in hasCtor and
hasDtor should be passed back to RuleClassType().</FONT></P></MENU>
<MENU><IMG src="Chapter 11 Constructors and Destructors.files/RULE13S.gif">
<P><FONT face=arial size=-1><B>Figure {RULE13S}</B> </FONT></P></MENU>Generating
a default constructor or default destructor is fairly simple. It involves using
the token queue, and an additional call to <TT>RuleProcDeclaration()</TT>.
Notice that the tokens are pushed on in reverse order. This is because we are
inserting them at the front of the queue. <PRE> if !hasCtor and needsCtor then
OutSymbol(constructorsy);
OutSymbol(endsy);
OutSymbol(beginsy);
OutSymbol(semicolonsy);
OutSymbol(rparentsy);
OutSymbol(lparentsy);
OutSymbol(constructorsy);
ProcFuncDeclaration (lkeys, classblock, FALSE);
end if;
if !hasDtor && needsDtor then
OutSymbol(destructorsy);
OutSymbol(endsy);
OutSymbol(beginsy);
OutSymbol(semicolonsy);
OutSymbol(rparentsy);
OutSymbol(lparentsy);
OutSymbol(destructorsy);
ProcFuncDeclaration (lkeys, classblock, FALSE);
end if;
</PRE>By now, the code at the end of <TT>RuleClassType()</TT> should be quite
confusing. We have listed it here with comments in order to better clarify what
is happening: <PRE> //*** parse "end class"
TestSymbol (endsy, ER_NOEND, keys);
TestSymbol (classsy, ER_NOQUALIF, keys);
//*** Check to see that something was actually declared
if totalsize = 0 then
WarningMsg("Size of class is zero or unknown");
end if;
//*** Set group properties
classblock->setGroupSize (totalsize); // Groupsize
classblock->setSize ( totalsize + classblock->getSharedSize()); // Total size
classblock->setVtblGroupSize(vproccount); // Vtable group size
classblock->AllocateShares(); // Partition group segments
classblock->modnum(table.ModNum); // Set mod # for meta-ctor
//*** Set ctorprocnum
classblock->setMetaCtorProcNum(++procNum);
//*** Set dtor procnum: dtorident is a FunctionIdent
dtorident= classblock->getDestructor();
if dtorident <> NO_CTOR_NEEDED and dtorident <> null then
classblock->setMetaDtorProcNum(dtorident->getProcNum());
end if;
//*** Set vtableprocnum
classblock->setVtblProcNum(++procNum);
//*** Tie the class's info to that of the identifier for the class
classident->setExtra (classblock);
//*** Create the meta-constructor
BuildConstructor(classblock);
//*** Back the table out to the previous scope
table.Leave_Block();
</PRE>We do a lot of work in <TT>RuleCtorDeclaration()</TT> and
<TT>RuleDtorDeclaration</TT>. We have to let the containing class know that each
new procedure is a constructor or destructor, and which constructor is default
if there is one. Destructors in SAL are virtual by default.
<P>
<MENU><IMG src="Chapter 11 Constructors and Destructors.files/RULE54S.gif">
<P><FONT face=arial size=-1><B>Figure {RULE54S}</B> </FONT></P></MENU>
<MENU><IMG src="Chapter 11 Constructors and Destructors.files/RULE55S.gif">
<P><FONT face=arial size=-1><B>Figure {RULE55S}</B> </FONT></P></MENU>The final
bit of coding for symbol tables is in making the super statement work. The
approach we will demonstrate here is to utilize the token queue. Our procedure
works like so:
<OL>
<LI>If we are in a class constructor:
<OL>
<LI>Process any super statements, keeping track of which base classes have
had a constructor called. We also make sure that the same base class is not
constructed twice.
<LI>Once all super statements have been processed loop all base classes
<I>in their order of declaration</I>. For each base class where a
constructor has not been called in the previous step, if the base class has
<I>any</I> constructor, call <I>the default constructor</I>. If there is no
default constructor, then give an error. The compiler can not know on its
own how to call a constructor that takes arguments. The only constructor
that can take zero arguments is the default constructor. The default
constructor can be called by pushing tokens to make a super statement.
</LI></OL>
<P></P>
<LI>Process statements, like a normal procedure or function.
<P></P>
<LI>If we are in a class destructor:
<OL>
<LI>Process any super statements, keeping track of which base classes have
had a destructor called. We also make sure that the same base class is not
destructed twice.
<LI>Once all super statements have been processed loop all base classes
<I>in the opposite order of declaration</I>. For each base class where a
destructor has not been called in the previous step, call the destructor.
The destructor can be called by pushing tokens for a super statement.
</LI></OL>
<P></P></LI></OL>There exists a function that performs each of these steps.
There is one for constructors called <TT>CallBaseClassCtors()</TT>, and one for
destructors called <TT>CallBaseClassDtors()</TT>. These functions also act like
rules. They work by processing as many super statements as are given, then using
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -