D language functions

xiaoxiao2021-03-06  41

function

Facts Functions All non-static non-private functions are virtual functions. This sounds well, but because the D compiler knows all class hierarchies when generating code, all unloaded functions can be optimized to non-virtual functions. In fact, because C programmers tend to "declare it for virtual functions", the method used "declares that the result is virtual function unless we can prove that it can be a non-virtual function" resulting more direct Function call. BUG caused by heavy duty non-virtual functions is also reduced.

Functions with non-D links will not be virtual functions, so they cannot be overloaded.

A function labeled Final cannot be overloaded in the derived class unless they are private. E.g:

Class A

{

INT DEF () {...}

Final Int foo () {...}

Final private int bar () {...}

Private Int ABC () {...}

}

Class B: a

{

INT DEF () {...} // ok, overload a.def

INT foo () {...} // error, A.foo is Final

INT bar () {...} // ok, a.bar is Final Private, but not Virtual

INT abc () {...} // ok, a.abc is not Virtual, B.ABC is Virtual

}

Void Test (a a) / / See the call to the argument below

{

A.def (); // Call B.def

A.foo (); // Call a.foo

a.bar (); // call a.bar

A.Abc (); // Call a.ABC

}

Void func ()

{B b = new b ();

Test (b);

}

Support Coordination Return Type, which means that the derived overload function can return to the type derived from the returned type of function:

Class a {}

Class B: a {}

Class foo

{

A test () {return null;}

}

Class Bar: foo

{

B Test () {return null;} // Rewind TEST (), and foo.test () has a Co-return type

}

Functions in the function inheritance and overload derived class The functions of the same parameter type with the function name in the base class:

Class A

{

INT foo (int x) {...}

}

Class B: a

{

OVERRIDE INT FOO (int x) {...}

}

Void test ()

{

B b = new b ();

Bar (b);

}

Void Bar (a a)

{

A.foo (); // Call B.foo (int)

}

However, when the overload analysis is performed, the functions in the base class are not considered:

Class A

{

INT foo (int x) {...}

INT foo (long y) {...}

}

Class B: a

{

Override int foo (long x) {...}

}

Void test ()

{

B b = new b ();

Bar (b);

}

Void Bar (a a)

{

A.foo (1); // Call a.foo (int)

B b = new b ();

B.foo (1); // Call B.foo (long) because a.foo (int) will not be considered

}

Consider the base class function during the overload analysis process, use

Alias ​​declaration:

Class A

{

INT foo (int x) {...}

INT foo (long y) {...}}

Class B: a

{

Alias ​​a.foo foo;

Override int foo (long x) {...}

}

Void test ()

{

B b = new b ();

Bar (b);

}

Void Bar (a a)

{

A.foo (1); // Call a.foo (int)

B b = new b ();

B.foo (1); // Call a.foo (int)

}

The default value of the function parameters will not be inherited:

Class A

{

Void foo (int x = 5) {...}

}

Class B: a

{

Void foo (int x = 7) {...}

}

Class C: B

{

Void foo (int x) {...}

}

Void test ()

{

A a = new a ();

A.foo (); // Call a.foo (5)

B b = new b ();

B.foo (); // Call B.FOO (7)

C c = new c ();

C.foo (); // error, C.foo requires a parameter

}

There is no inline keyword in the inline function D. The compiler decides whether to inline a function, just like the Register keyword no longer associates whether the variable has a register in the compiler. (There is no register keyword.)

The function is overloaded in C , and the function is overloaded with a lot of complex levels, and some are defined as "better" match. If the code writer uses a function to overload a more subtle behavior, the code will become difficult to maintain. Not only C experts are difficult to understand why choose this function without choosing which, different C compilers may also use different ways to implement this feature, which causes subtle and catastrophic results.

In D, the function overload is simple. Allows precise matching, allowing the matching of implicit conversions to be included, in addition to this, do not match. If there is more than one match, it is an error.

The non-link function is not allowed to be overloaded.

Function parameter parameters are

in,

OUT or

inout.

IN is the default;

OUT and

The inout parameter works like a storage class. E.g:

Int foo (int X, Out Int Y, INT INT Z, INT Q);

X is

in, y is

OUT, Z is

Inout, and Q is

IN.

OUT is rarely seen, INOUT is less, so if you use these two features, you need to use the keyword, and you will use the default value. They appear in D because:

Function declaration clearly represents which is the input of functions, which is the output of functions. No longer need to use a named IDL language separately. You can provide more information to the compiler, so you can provide a better error check and generate better code. (Maybe) does not need to be referenced ('&' in C ) declaration.

The OUT parameter is set to the default value of the corresponding type. E.g:

Void foo (Out Int Bar)

{

}

INT bar = 3;

Foo (bar);

// bar is now 0

Variable function parameter functions can be variable, that is, they can have any parameters. A variable function is represented by '...' behind the required parameters:

Int foo (int x, int y, ...);

Foo (3, 4); // ok

FOO (3, 4, 6.8); // OK, a variable parameter

Foo (2); // Error, Y is the required parameter with a non-link code must have at least one non-variable parameter. INT abc (...); // ok, d link

Extern (c) def (...); // Error, must have at least one parameter variable function declare a special local variable,

_Argptr, it is pointing to the first variable parameter

Void * Type pointer. If you want to access these parameters,

_Argptr must be converted to pointers to the desired parameter type:

FOO (3, 4, 5); // The first variable parameter is 5

Int foo (int x, int y, ...)

{INT Z;

z = * Cast (int *) _ argptr; // z is set to 5

}

For variable functions that use D link, there is an implicit name

_Arguments parameters, its type

TypeInfo [].

_Arguments provides the number of parameters, and also provides the type of each parameter so that the type of security can be written.

Class foo {}

Void foo (int x, ...)

{

Printf ("% D arguments / n", _Arguments.length;

For (int i = 0; i <_ARGUMENTS.LENGTH; i )

{

_arguments [i] .print ();

IF (_arguments [i] == typeid (int))

{

INT J = * CAST (int *) _ argptr;

_Argptr = int.sizeof;

Printf ("/ t% d / n", j);

}

Else IF (_Arguments [i] == TypeId (long))

{

Long J = * CAST (long *) _ argptr;

_Argptr = long.sizeof;

Printf ("/ T% LLD / N", J);

}

Else IF (_Arguments [i] == TypeId (double))

{

Double D = * CAST (double *) _ argptr;

_Argptr = double.sizeof;

Printf ("/ T% g / n", d);

}

Else IF (_Arguments [i] == TypeId (foo))

{

Foo f = * CAST (foo *) _ argptr;

_Argptr = foo.sizeof;

Printf ("/ t% p / n", f);

}

Else

Assert (0);

}

}

void main ()

{

Foo f = new foo ();

Printf ("% p / n", f);

FOO (1, 2, 3l, 4.5, f);

}

Will be printed:

00870fd0

4 arguments

int

2

Long

3

Double

4.5

Foo

00870fd0

In order to avoid the trouble brought by various stacked stack distributions on different CPU structures, use

STD.STDARG Access variable parameters:

IMPORT std.stdarg;

Void foo (int x, ...)

{

Printf ("% D arguments / n", _Arguments.length;

For (int i = 0; i <_ARGUMENTS.LENGTH; i )

{

_arguments [i] .print (); if (_Arguments [i] == typeid (int))

{

INT j = va_arg! (int) (_ argptr);

Printf ("/ t% d / n", j);

}

Else IF (_Arguments [i] == TypeId (long))

{

Long J = VA_ARG! (long) (_ argptr);

Printf ("/ T% LLD / N", J);

}

Else IF (_Arguments [i] == TypeId (double))

{

Double D = VA_ARG! (_ _ argptr);

Printf ("/ T% g / n", d);

}

Else IF (_Arguments [i] == TypeId (foo))

{

Foo f = va_arg! (Foo) (_ argptr);

Printf ("/ t% p / n", f);

}

Else

Assert (0);

}

}

Local variables will be considered an error if using a local variable that is not assigned. The implementation of the compiler may not always be detected. The compiler of other languages ​​sometimes warns this, but because this situation almost always means that there is bug, so it should be processed as an error.

If a variable is declared, it will be considered an error. Dead variables, like the dead code that is over-time, will make the maintainer confused.

If a variable declared masks another variable in a unified function, it will be considered an error:

Void Func (INT X)

{INT X; / / Error, covering the X of the previous definition

Double Y;

...

{char y; // Error, cover Yes in the previous definition

Int z;

}

{wchar z; // legal, the front z has been detached from the role domain}

}

Because this situation does not seem reasonable, it is not a bug that appears in practice in practice.

If a local variable is returned, it is considered an error. (Translation: How many C books have warned you not to do this? If not, you can throw away the book. In D, you no longer need to worry, you lose, just a little extremely low-level ability.)

If the local variable is the same name with the label, it will be considered an error.

Nested function functions can nested in other functions:

Int bar (INT A)

{

Int foo (int b)

{

INT abc () {Return 1;}

RETURN B ABC ();

}

Return foo (a);

}

Void test ()

{

INT I = BAR (3); // i is assigned 4

}

Nested functions can only be accessed by a function of its periphery and a nested function with the same nested depth:

Int bar (INT A)

{

INT foo (int b) {RETURN B 1;}

INT ABC (INT B) {Return Foo (B);} // ok

Return foo (a);

}

Void test ()

{

INT I = BAR (3); // ok

INT j = bar.foo (3); // error, bar.foo is invisible

}

Nested functions can access variables of its periphery and other symbols. Here, "Access" means that it can be read or written.

Int bar (INT A)

{INT C = 3;

INT foo (int b) {

B = C; // 4 is added to B

C ; // bar.c is now 5

RETURN B C; // Return 12

}

C = 4;

INT i = foo (a); // i is photographed 12

Return I C; // Return 17

}

Void test ()

{

INT i = bar (3); // i is assigned 17

}

This access capability can sprinkle multiple nested:

Int bar (INT A)

{INT C = 3;

Int foo (int b)

{

Int abc ()

{

Return C; // Access bar.c

}

RETURN B C ABC ();

}

Return foo (3);

}

Static nested functions cannot access any stack variables of the peripheral function, but can access static variables. This behavior is similar to the static member function.

Int bar (INT A)

{INT C;

Static int D;

Static Int Foo (INT B)

{

B = D; // ok

B = C; // error, foo () cannot access the stack frame of BAR ()

RETURN B 1;

}

Return foo (a);

}

The function can be nested in the member function:

Struct foo

{INT A;

Int bar ()

{INT C;

Int foo ()

{

Return C A;

}

}

}

Member functions of nested structures and nested classes cannot access the stack variables of the peripheral function, but can access other symbols:

Void test ()

{INT J;

Static int S;

Struct foo

{INT A;

Int bar ()

{INT C = S; // ok, s is static

INT D = J; // Error, you cannot access the stack frame of TEST ()

Int foo ()

{

INT E = S; // OK, S is static

INT f = j; // error, you cannot access the stack frame of TEST ()

Return C a; // OK, bar () stack frame is accessible

/ / THIS pointer to foo.bar ()

/ / I can access the FOO member

}

}

}

}

Nested functions always use the D function link type.

Different from the module-level declaration, the declaration of the function scope will be handled in order of the statement. This means that even a nested function cannot be called each other:

Void test ()

{

Void foo () {bar ();} // error, bar is undefined

Void bar () {foo ();} // ok

}

The solution is to use the entrustment:

Void test ()

{

Void delegate () fp;

Void foo () {fp ();

Void bar () {foo ();

FP = & bar;

}

The direction of the future: This limit may be deleted.

Principal, function pointer and dynamic closure function pointer can point to a static nested function:

INT function () fp;

Void test ()

{Static Int a = 7;

Static int foo () {Return A 3;

FP = & foo;

}

Void bar ()

{

Test ();

INT i = fp (); // i is set to 10

}

Entrusted can be assigned with non-static nested functions:

INT delegate () DG; void test ()

{INT A = 7;

INT foo () {Return A 3;

DG = & foo;

INT i = DG (); // i is set to 10

}

However, once the function of the stack variable is declared, the stack variable is no longer effective; the same, pointers pointing to the stack variable are no longer effective:

INT * BAR () {Int B; test (); int i = DG (); // error, test.a no longer exists Return & B; // error, bar.b is no longer effective after BAR () exits} Delegation of non-static nested functions includes two data: pointers pointing to peripheral function stack frames (called

Frame pointer) and the address of the function. Similar to this, structural / class non-static member functions are entrusted by

The address of the THIS pointer and the member function is composed. These two forms of commission can be converted to each other, in fact they have the same type:

Struct foo

{INT A = 7;

Int bar () {return a;}

}

INT foo (int de delegate () DG)

{

Return DG () 1;

}

Void test ()

{

INT x = 27;

INT ABC () {Return x;}

Foo f;

INT I;

i = foo (& ABC); // i is set to 28

i = fuo (& f.bar); // i is set to 8

}

The combination of environment and functions is called

Dynamic closure.

Future direction: Function pointers and commission may be merged into a unified syntax and can be replaced with each other.

Anonymous function and anonymous commission see function text.

转载请注明原文地址:https://www.9cbs.com/read-58563.html

New Post(0)