The D programming language function inheritance and overriding

Cours the D programming language function inheritance and overriding, tutoriel & guide de travaux pratiques en pdf.

Functions
Virtual Functions

All non-static non-private member functions are virtual. This may sound inefficient, but since the D compiler knows all of the class hierarchy when generating code, all functions that are not overridden can be optimized to be non-virtual. In fact, since C++ programmers tend to « when in doubt, make it virtual », the D way of « make it virtual unless we can prove it can be made nonvirtual » results on average much more direct function calls. It also results in fewer bugs caused by not declaring a function virtual that gets overridden.
Functions with non-D linkage cannot be virtual, and hence cannot be overridden.
Functions marked as final may not be overridden in a derived class, unless they are also private. For example:
class A
{
int def() { … }
final int foo() { … }
final private int bar() { … }
private int abc() { … }
}
class B : A
{
int def() { … } // ok, overrides 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)
{
a.def(); // calls B.def
a.foo(); // calls A.foo
a.bar(); // calls A.bar
a.abc(); // calls A.abc
}
void func()
{ B b = new B();
test(b);
}
Covariant return types are supported, which means that the overriding function in a derived class
can return a type that is derived from the type returned by the overridden function:
class A { }
class B : A { }
class Foo
{
A test() { return null; }
}
class Bar : Foo
{
B test() { return null; } // overrides and is covariant with
Foo.test()
}

Function Inheritance and Overriding

A functions in a derived class with the same name and parameter types as a function in a base class overrides that function:
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(); // calls B.foo(int)
}
However, when doing overload resolution, 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); // calls A.foo(int)
B b = new B();
b.foo(1); // calls B.foo(long), since A.foo(int) not
considered
}
To consider the base class’s functions in the overload resolution process, use an AliasDeclaration:
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); // calls A.foo(int)
B b = new B();
b.foo(1); // calls A.foo(int)
}
A function parameter’s default value is not 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(); // calls A.foo(5)
B b = new B();
b.foo(); // calls B.foo(7)
C c = new C();
c.foo(); // error, need an argument for C.foo
}

Inline Functions

There is no inline keyword. The compiler makes the decision whether to inline a function or not, analogously to the register keyword no longer being relevant to a compiler’s decisions on enregistering variables. (There is no register keyword either.)

Function Overloading
In C++, there are many complex levels of function overloading, with some defined as « better » matches than others. If the code designer takes advantage of the more subtle behaviors of overload function selection, the code can become difficult to maintain. Not only will it take a
C++ expert to understand why one function is selected over another, but different C++ compilers can implement this tricky feature differently, producing subtly disastrous results.
In D, function overloading is simple. It matches exactly, it matches with implicit conversions, or it does not match. If there is more than one match, it is an error.
Functions defined with non-D linkage cannot be overloaded.

Function Parameters
Parameters are in, out, or inout. in is the default; out and inout work like storage classes. For example:
int foo(int x, out int y, inout int z, int q);
x is in, y is out, z is inout, and q is in.
out is rare enough, and inout even rarer, to attach the keywords to them and leave in as the default. The reasons to have them are:
• The function declaration makes it clear what the inputs and outputs to the function are.
• It eliminates the need for IDL as a separate language.
• It provides more information to the compiler, enabling more error checking and possibly better code generation.
• It (perhaps?) eliminates the need for reference (&) declarations. out parameters are set to the default initializer for the type of it. For example:
void foo(out int bar)
{
}
int bar = 3;
foo(bar);
// bar is now 0

Variadic Function Parameters
Functions can be variadic, meaning they can take an arbitrary number of parameters. A variadic function is declared as taking a parameter of … after the required function parameters:
int foo(int x, int y, …);
foo(3, 4); // ok
foo(3, 4, 6.8); // ok, one variadic argument
foo(2); // error, y is a required argument
Variadic functions with non-D linkage must have at least one non-variadic parameter declared.
int abc(…); // ok, D linkage
extern (C) def(…); // error, must have at least one parameter
Variadic functions have a special local variable declared for them, _argptr, which is a void*
pointer to the first of the variadic arguments. To access the arguments, _argptr must be cast to a
pointer to the expected argument type:
foo(3, 4, 5); // first variadic argument is 5
int foo(int x, int y, …)
{ int z;
z = *cast(int*)_argptr; // z is set to 5
}
For variadic functions with D linkage, an additional hidden argument with the name _arguments
and type TypeInfo[] is passed to the function. _arguments gives the number of arguments and the type of each, enabling the creation of typesafe variadic functions.
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);
}
which prints:
00870FD0
To protect against the vagaries of stack layouts on different CPU architectures, use std.stdarg to
access the variadic arguments:
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!(double)(_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
It is an error to use a local variable without first assigning it a value. The implementation may not always be able to detect these cases. Other language compilers sometimes issue a warning for this, but since it is always a bug, it should be an error.
It is an error to declare a local variable that is never referred to. Dead variables, like anachronistic dead code, is just a source of confusion for maintenance programmers.
It is an error to declare a local variable that hides another local variable in the same function:
void func(int x)
{ int x; error, hides previous definition of x
double y;

{ char y; error, hides previous definition of y
int z;
}
{ wchar z; legal, previous z is out of scope
}
}
While this might look unreasonable, in practice whenever this is done it either is a bug or at least looks like a bug.
It is an error to return the address of or a reference to a local variable.
It is an error to have a local variable and a label with the same name.

Introduction
Overview
Lexical
Modules
Declarations
Types
Properties
Attributes
Pragmas
Expressions
Statements
Arrays
Structs & Unions
Classes
Interfaces
Enums
Functions
Operator Overloading
Templates
Mixins
Contracts
Versioning
Handling errors
Garbage Collection
Memory Management
Floating Point
Inline Assembler
Interfacing To C
Portability Guide
Embedding D in HTML
Application Binary Interface
Phobos (Runtime Library)
Example: wc
D Compiler
Future
D Change Log
Acknowledgements
Comparisons
D vs C/C++/C#/Java

Cours gratuitTélécharger le cours complet

Télécharger aussi :

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *