`
hqs7636
  • 浏览: 222683 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

函 数 2.014

阅读更多
[size=large]函数体:
块语句
语句体
In语句 语句体
Out语句 语句体
In语句 Out语句 语句体
Out语句 In语句 语句体

In语句:
in 语句块

Out语句:
out 语句块
out ( 标识符 ) 语句块

语句块:
body 语句块

  2.014

Pure Functions

Pure functions are functions that produce the same result for the same arguments. To that end, a pure function: 

has parameters that are all invariant or are implicitly convertible to invariant 
does not read or write any global mutable state 

A pure function can throw exceptions and allocate memory via a NewExpression. 

int x;
invariant int y;
const int* pz;

pure int foo(int i,     // ok, implicitly convertible to invariant
             char* p,         // error, not invariant
             const char* q,   // error, not invariant
             invariant int* s // ok, invariant
            )
{
    x = i;    // error, modifying global state
    i = x;    // error, reading mutable global state
    i = y;    // ok, reading invariant global state
    i = *pz;  // error, reading const global state
    return i;
}


Nothrow Functions

Nothrow functions do not throw any exceptions derived from class Exception. 



1 虚函数(Virtual Functions)

虚函数指的是这样一类函数:它们通过叫作 vtbl[] 的 函数指针表 被间接调用,而非直接进行调用。所有的非静态、非私有、非模板 成员函数都是虚函数。这听起来也许有些低效,但是,因为 D 编译器在生成代码时知道所有的类层次结构,所以,所有未被重写的函数可以被优化成非虚函数。

事实上,C++ 程序员倾向于“在不确定时,声明它为虚函数”;

而 D 采用的方式是“都声明成虚函数,除非我们可以证明它可以是非虚函数”,这样综合后的结果就是产生更多更直接的函数调用。这也可以大大减少由于没有将会被重写(voerride)的函数声明为虚函数,而引起的错漏。

因为带有“非 D 连接特性(non-D linkage)”的函数不能是虚函数,因此不能被重写。

因为成员模板函数不能是虚的,因此不能被重写。

标记为 final 的函数不可以在派生类中被重写,除非它们同时也是 private 类型。

例如:
class A
{
int def() { ... }
final int foo() { ... }
final private int bar() { ... }
private int abc() { ... }
}

class B : A
{
int def() { ...} // 正确,重写 A.def
int foo() { ...} // 错误,A.foo 是 final 的
int bar() { ...} // 正确,A.bar 是 final private 的,但不是 virtual 的
int abc() { ...} // 正确,A.abc 不是 virtual 的,B.abc 是 virtual 的
}

void test(A a)
{
a.def(); // 调用 B.def
a.foo(); // 调用 A.foo
a.bar(); // 调用 A.bar
a.abc(); // 调用 A.abc
}

void func()
{ B b = new B();
test(b);
}

协变返回类型(covariant return types)”,即表示在 派生类 里的重写函数可以返回这样一种类型——此类型派生自 被重写函数 所返回的类型:

class A { }

class B : A { }

class Foo
{
A test() { return null; }
}

class Bar : Foo
{
B test() { return null; } // 重写,并且同 Foo.test() 是协变的
}

2.014
Virtual functions all have a hidden parameter called the this reference, which refers to the class object for which the function is called.



2 函数继承(inheritance)和重写(overidding)

派生类中的函数将重载 基类中 同函数名、同参数类型 的函数:

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(1); // 调用 B.foo(int)
          //2.014  issues runtime error (instead of calling A.foo(int))
}

而且,在进行重载解析的时候,基类中对应的那些函数都不会被考虑的:

class A
{
int foo(int x) { ... }
int foo(long y) { ... }
}

class B : A
{
override int foo(long x) { ... }
}

void test()
{
B b = new B();
b.foo(1); // 调用 B.foo(long),因为不会考虑 A.foo(int)
A a = b;
a.foo(1); // calls A.foo(int)
}

要在重载解析过程中让基类的那些函数有效,请使用“别名声明”:

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); // 调用 A.foo(int)
B b = new B();
b.foo(1); // 调用 A.foo(int)
}


 2.014
If such an AliasDeclaration is not used, the derived class's functions completely override all the functions of the same name in the base class, even if the types of the parameters in the base class functions are different. If, through implicit conversions to the base class, those other functions do get called, an std.HiddenFuncError exception is raised: 

import std.hiddenfunc;

class A
{
     void set(long i) { }
     void set(int i)  { }
}
class B : A
{
     void set(long i) { }
}

void foo(A a)
{   int i;
    try
    {
        a.set(3);   // error, throws runtime exception since
                    // A.set(int) should not be available from B
    }
    catch (HiddenFuncError o)
    {
	i = 1;
    }
    assert(i == 1);
}

void main()
{
    foo(new B);
}

If an HiddenFuncError exception is thrown in your program, the use of overloads and overrides needs to be reexamined in the relevant classes.



函数参数的默认值不会被继承的:
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(); // 调用 A.foo(5)
B b = new B();
b.foo(); // 调用 B.foo(7)
C c = new C();
c.foo(); // 错误,C.foo 需要一个参数
}


3 内联函数(Inline Functions)

D 中没有 inline 关键字。编译器决定是否将一个函数内联,就像 register 关键字不再同编译器是否将变量存在 寄存器中 相关一样。(也没有 register 关键字。)


4 函数重载(Function Overloading)

在 C++ 中,函数重载有很多复杂的级别,一些被定义为“更好的”匹配。如果代码编写者
利用函数重载选择时更为细微的行为,代码就会变得难以维护。不仅 C++ 专家很难弄明白
为什么选择这个函数而不选择哪个,不同的 C++ 编译器也可能会采用不同的方式实现这个
充满技巧的特征,这造成了微妙而灾难性的结果。

在 D 中,函数重载很简单。允许精确匹配,允许包含隐式转换的匹配,除此之外就不匹
配。

如果有多于一个匹配,就是错误。

非 D 链接的函数不允许重载。

上一节别改成如下的:2.014

Function Overloading

Functions are overloaded based on how well the arguments to a function can match up with the parameters. The function with the best match is selected. The levels of matching are: 

no match 
match with implicit conversions 
match with conversion to const 
exact match 

Each argument (including any this pointer) is compared against the function's corresponding parameter, to determine the match level for that argument. The match level for a function is the worst match level of each of its arguments.

If two or more functions have the same match level, then partial ordering is used to try to find the best match. Partial ordering finds the most specialized function. If neither function is more specialized than the other, then it is an ambiguity error. Partial ordering is determined for functions f() and g() by taking the parameter types of f(), constructing a list of arguments by taking the default values of those types, and attempting to match them against g(). If it succeeds, then g() is at least as specialized as f(). For example: 

class A { }
class B : A { }
class C : B { }
void foo(A);
void foo(B);

void test()
{
  C c;
  /* Both foo(A) and foo(B) match with implicit conversion rules.
   * Applying partial ordering rules,
   * foo(B) cannot be called with an A, and foo(A) can be called
   * with a B. Therefore, foo(B) is more specialized, and is selected.
   */
  foo(c); // calls foo(B)
}

A function with a variadic argument is considered less specialized than a function without. 

Functions defined with non-D linkage cannot be overloaded. because the name mangling does not take the parameter types into account. 

Overload Sets
Functions declared at the same scope overload against each other, and are called an Overload Set. A typical example of an overload set are functions defined at module level: 

module A;
void foo() { }
void foo(long i) { }
A.foo() and A.foo(long) form an overload set. A different module can also define functions with the same name: 

module B;
class C { }
void foo(C) { }
void foo(int i) { }
and A and B can be imported by a third module, C. Both overload sets, the A.foo overload set and the B.foo overload set, are found. An instance of foo is selected based on it matching in exactly one overload set: 

import A;
import B;

void bar(C c)
{
    foo();    // calls A.foo()
    foo(1L);  // calls A.foo(long)
    foo(c);   // calls B.foo(C)
    foo(1,2); // error, does not match any foo
    foo(1);   // error, matches A.foo(long) and B.foo(int)
    A.foo(1); // calls A.foo(long)
}

Even though B.foo(int) is a better match than A.foo(long) for foo(1), it is an error because the two matches are in different overload sets. 

Overload sets can be merged with an alias declaration:

import A;
import B;

alias A.foo foo;
alias B.foo foo;

void bar(C c)
{
    foo();    // calls A.foo()
    foo(1L);  // calls A.foo(long)
    foo(c);   // calls B.foo(C)
    foo(1,2); // error, does not match any foo
    foo(1);   // calls B.foo(int)
    A.foo(1); // calls A.foo(long)
}






4.1 函数形式参数(Function Parameters)

这些参数可以是 in、out、ref 或者 lazy。默认是 in;其它的参数工作起来跟存储类别
(storage class)一样。

例如:
int foo(int x, out int y, ref int z, int q);

x 为 in,y 为 out,z 为 ref,而 q 为 in。

out 已经很少见了,而 ref 则更少见,因此如果使用这些特性就需要使用关键字,而将 in
作为默认值。

上面这段被改成:2.014

Parameter storage classes are in, out, ref, lazy, final, const, invariant, or scope. 

For example: 
int foo(in int x, out int y, ref int z, int q);

x is in, y is out, z is ref, and q is none. 

The in storage class is equivalent to const scope. 

If no storage class is specified, the parameter becomes a mutable copy of its argument. 


• 函数声明清楚的表示了哪些是函数的输入,哪些是函数的输出。
• 不再需要单独使用一种叫 IDL 语言。
• 可以给编译器提供更多的信息,从而可以提供更好的错误检查并生成更好的代码。

out 参数被设为对应类型的默认值。

例如:
void foo(out int x)
{
// 在 foo() 的开始,x 被设置为 0
}
int a = 3;
foo(a);
// a 现在为 0

void abc(out int x)
{
x = 2;
}

int y = 3;
abc(y);
// y 现在为 2

void def(ref int x)
{
x += 1;
}

int z = 3;
def(z);
// z 现在为 4

对于通过引用传递的动态数组和对象参数,in/out/ref 只会作用到 该引用上, 而不会是那些内容里。

懒式参数被求值的时间不是以函数被调用时,而是参数在函数内被求值时进行的。因此,懒
式参数会被计算 0 次或多次。

懒式参数不能是一个左值。

void dotimes(int n, lazy void exp)
{
while (n--)
exp();
}

void test()
{ int x;
dotimes(3, writefln(x++));
}

控制台输出:
01
2

void 类型的懒式参数不能接受一个任何类型的参数。


5 参数可变型函数

那些带有可变数目参数的函数就叫做参数可变型(variadic)函数。

一个参数可变型函数可以有下面三种形式:

1. C-风格的参数可变型函数
2. 带有类型信息的参数可变型函数
3. 类型安全的参数可变型函数


5.1 C-风格的参数可变型函数

C-风格的参数可变型函数被声明时在所必需的函数参数里带有“...”参数。它有一个非D连
接属性,如 extern (C):

extern (C) int foo(int x, int y, ...);
foo(3, 4); // 正确
foo(3, 4, 6.8); // 正确,参数可变型函数
foo(2); // 错误,y 是一个必需的参数

必须至少声明一个固定型参数。

extern (C) int def(...); // 错误,必须至少有一个参数

C-风格的参数可变型函数符合 C 对于参数可变型函数的调用协定,而且对于调用像
printf 那样的 C 库函数最有用。这些参数可变型函数的实现会声明一个特殊的局部变
量:_argptr,此变量是一个指向可变参数里的第一个的 void* 型指针。

要访问这些参数,_argptr 就必须被转换成一所期望参数类型的指针:

foo(3, 4, 5); // 第一个可变型参数是 5
int foo(int x, int y, ...)
{ int z;
z = *cast(int*)_argptr; // z 被设置为 5
}

为了避开不同 CPU 结构上的各种奇特的堆栈分布带来的麻烦,请使用 std.c.stdarg 访问那些可变(variadic)参数:

import std.c.stdarg;


5.2 D-风格的参数可变型函数

带有参数和类型信息的参数可变型函数声明方式:在必需的函数参数之后带上参数“...”。

它有 D 连接属性,因此不需要声明任何非可变型参数:

int abc(char c, ...); // 一个必需的参数: c
int def(...); // 正确

这些参数可变型函数会声明一个特殊的局部变量:_argptr,此变量是一个指向第一个可变
参数的 void* 型指针。要访问这些参数,_argptr 就必须被转换成一所期望参数类型的指
针:

foo(3, 4, 5); // 第一个可变型参数是 5
int foo(int x, int y, ...)
{ int z;
z = *cast(int*)_argptr; // z 被设置为 5
}

一个名叫 _arguments 而类型为 TypeInfo[] 的额外的隐藏参数会被传递给该函
数。_arguments 会给出参数的数目以及每一个的类型,这样可以创建类型安全的 variadic 函数。

import std.stdio;

class Foo { int x = 3; }

class Bar { long y = 4; }

void printargs(int x, ...)
{
writefln("%d arguments", _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;
writefln("\t%d", j);
}

else if (_arguments[i] == typeid(long))
{
long j = *cast(long *)_argptr;
_argptr += long.sizeof;
writefln("\t%d", j);
}

else if (_arguments[i] == typeid(double))
{
double d = *cast(double *)_argptr;
_argptr += double.sizeof;
writefln("\t%g", d);
}

else if (_arguments[i] == typeid(Foo))
{
Foo f = *cast(Foo*)_argptr;
_argptr += Foo.sizeof;
writefln("\t%X", f);
}

else if (_arguments[i] == typeid(Bar))
{
Bar b = *cast(Bar*)_argptr;
_argptr += Bar.sizeof;
writefln("\t%X", b);
}

else
assert(0);
}
}

void main()
{
Foo f = new Foo();
Bar b = new Bar();
writefln("%X", f);
printargs(1, 2, 3L, 4.5, f, b);
}

其输出为:

00870FE0
5 arguments
int
2
long
3
double
4.5
Foo
00870FE0
Bar
00870FD0

为了避开不同 CPU 结构上的各种奇特的堆栈分布带来的麻烦,请使用 std.stdarg 访问那些可变(variadic)参数:

import std.stdio;
import std.stdarg;

void foo(int x, ...)
{
writefln("%d arguments", _arguments.length);
for (int i = 0; i < _arguments.length; i++)
{ _arguments[i].print();

if (_arguments[i] == typeid(int))
{
int j = va_arg!(int)(_argptr);
writefln("\t%d", j);
}

else if (_arguments[i] == typeid(long))
{
long j = va_arg!(long)(_argptr);
writefln("\t%d", j);
}

else if (_arguments[i] == typeid(double))
{
double d = va_arg!(double)(_argptr);
writefln("\t%g", d);
}

else if (_arguments[i] == typeid(FOO))
{
FOO f = va_arg!(FOO)(_argptr);
writefln("\t%X", f);
}

else
assert(0);
}
}


5.3 类型安全的参数可变型函数

使用类型安全的参数可变型函数的情形是:参数的可变部分被用于创建一个数组或一个类对
象。

对于数组:
int test()
{
return sum(1, 2, 3) + sum(); // 返回 6+0
}

int func()
{
int[3] ii = [4, 5, 6];
return sum(ii); // 返回 15
}

int sum(int[] ar ...)
{
int s;
foreach (int x; ar)
s += x;
return s;
}

对于静态数组:
int test()
{
return sum(2, 3); // 错误,对于数组需要 3 个值
return sum(1, 2, 3); // 返回 6
}

int func()
{
int[3] ii = [4, 5, 6];
int[] jj = ii;
return sum(ii); // 返回 15
return sum(jj); // 错误,类型不匹配
}

int sum(int[3] ar ...)
{
int s;
foreach (int x; ar)
s += x;
return s;
}

对于类对象:
class Foo
{
int x;
char[] s;
this(int x, char[] s)
{
this.x = x;
this.s = s;
}
}

void test(int x, Foo f ...);
...
Foo g = new Foo(3, "abc");
test(1, g); // 正确,因为 g 是 Foo 的实例
test(1, 4, "def"); // 正确
test(1, 5); // 错误,不能匹配 Foo 的构造函数

实现可能在堆栈上构造对象或数组实例。因此,在参数可变型返回之后 引用该实例 就会出现
一个错误:
Foo test(Foo f ...)
{
return f; // 错误,在返回之后 f 实例内容无效
}

int[] test(int[] a ...)
{
return a; // 错误,在返回之后数组内容无效
return a[0..1]; // 错误,在返回之后数组内容无效
return a.dup; // 正确,因为做了复本
}

对于其它类型,参数会自己建立,就像下面的:
int test(int i ...)
{
return i;
}
...
test(3); // 返回 3
test(3, 4); // 错误,太多参数
int[] x;
test(x); // 错误,类型不匹配


5.4 懒式参数可变型函数

如果可变型参数是一个不带参数的委托数组:

void foo(int delegate()[] dgs ...);

那么跟该委托的类型不匹配的每一个参数会被转换成一个委托。

int delegate() dg;
foo(1, 3+x, dg, cast(int delegate())null);

同等于:
foo( { return 1; }, { return 3+x; }, dg, null );


6 局部变量

如果使用一个未被赋值过的局部变量,会被视为错误。编译器的实现未必总能够检测到这些
情况。其他语言的编译器有时会为此发出警告,但是因为这种情况几乎总是意味着存在错
漏,所以应该把它处理为错误。

如果声明一个变量但却从未使用,会被视为错误。死变量,如同过时的死代码一样,都会使
维护者迷惑。

如果声明的一个变量掩盖了统一函数中的另一个变量,会被视为错误:

void func(int x)
{ int x; 错误,掩盖了前面定义的 x
double y;
...
{ char y; 错误,掩盖了前面定义的 y
int z;
}
{ wchar z; 合法,上一个 z 超出了作用域
}
}

因为这种情况看起来不合理,在实践中出现这种情况时不是一个错漏至少看起来也像一个错
漏。

如果返回一个局部变量的地址或引用,会被视为错误。

如果局部变量同标签同名,会被视为错误。


[/size]

。。。。。。。。。。。
分享到:
评论

相关推荐

    艺术ppt-素材 014.pptx

    ### 2. 广告设计资源的应用场景 #### 内容概述: 文件中提到了一系列广告设计资源,包括海报、易拉宝、展板等,这些资源可以用于不同的商业宣传活动。 #### 关键知识点: - **海报**:适用于各种宣传场合,如产品...

    VB编程资源大全(源码 其它3)

    o029_snow.zip 一个雪花飘落例子(3KB) 626,o028_zoomsys.zip 类似画图放大镜的东西(2KB) 627,o027_wiz2.zip 制作应用程序向导的例子(类似安装程序的界面)!(5KB) 628,o025_StopWatch.zip “跑表...

    HR常用表格3.pdf

    2. **应聘人员登记表(HR-002)**:求职者在申请职位时填写,包括个人信息、教育背景、工作经验、技能特长等,帮助HR初步了解候选人的基本情况。 3. **面试评估表(HR-003)**:面试官根据应聘者的面试表现进行评分...

    人力资源部HR常用表格模板.docx

    #### 2. 应聘人员登记表 (JQSW-HR-002) - **用途**:收集应聘者的个人信息及背景资料,作为初步筛选的依据。 - **主要内容**:包括个人基本信息(如姓名、性别、年龄等)、教育背景、工作经验、技能特长等。 - **...

    VB编程资源大全(源码 其它2)

    o029_snow.zip 一个雪花飘落例子(3KB) 626,o028_zoomsys.zip 类似画图放大镜的东西(2KB) 627,o027_wiz2.zip 制作应用程序向导的例子(类似安装程序的界面)!(5KB) 628,o025_StopWatch.zip “跑表...

    JDK 1.5的泛型實現(Generics in JDK 1.5)

    JDK 1.5的泛型實現(Generics in JDK 1.5) 1 侯捷觀點 JDK 1.5的泛型實現 .......................讓我們把帶有「參數化型別」...#014 this(10); #015 } #016 ... #017 } 圖 9a / JDK1.5的 java.util.ArrayList源碼 ...

    VB编程资源大全(源码 其它1)

    o029_snow.zip 一个雪花飘落例子(3KB) 626,o028_zoomsys.zip 类似画图放大镜的东西(2KB) 627,o027_wiz2.zip 制作应用程序向导的例子(类似安装程序的界面)!(5KB) 628,o025_StopWatch.zip “跑表...

    VB编程资源大全(源码 其它4)

    o029_snow.zip 一个雪花飘落例子(3KB) 626,o028_zoomsys.zip 类似画图放大镜的东西(2KB) 627,o027_wiz2.zip 制作应用程序向导的例子(类似安装程序的界面)!(5KB) 628,o025_StopWatch.zip “跑表...

    新型膦酸自组装薄膜(SAMs)应用于n型苝二酰亚胺(PDI)有机场效应晶体管

    文章提到的实验结果显示,在空气环境下,使用膦酸SAMs修饰的绝缘层,尤其是使用ODPA(可能是一种具体的膦酸衍生物)的SAMs,显著提高了基于PDI-i8C的OFETs的电子迁移率,达到了0.014 cm^2 V^-1 s^-1,相较于未修饰的...

    湖南省醴陵二中醴陵四中2017_2018学年高二数学下学期期中联考试题文20180511122

    2. **函数的性质**:函数的连续性和零点的概念,比如题目中的函数`f(x)`的零点问题,要求找到函数在给定区间内零点的个数,这涉及到函数的零点定理和介值定理。 3. **坐标变换**:曲线`y=sin2x`经过伸缩变换后的...

    新课标版2015_2016学年高一数学上学期第三次月考试题

    2. **幂函数**:题目提到了函数32)32()(mxmxf是幂函数。幂函数的形式为y = x^m,其中m是常数。根据题目,m = 3或m = 2,但题目要求m的值,所以m = 1是幂函数的唯一可能值。 3. **函数零点**:题目中求函数f(x...

Global site tag (gtag.js) - Google Analytics