See also: Programming in D for C Programmers
class Foo { Foo(int x); };
class Foo { this(int x) { } }which reflects how they are used in D.
class A { A() {... } }; class B : A { B(int x) : A() // call base constructor { ... } };
class A { this() { ... } } class B : A { this(int x) { ... super(); // call base constructor ... } }It's superior to C++ in that the base constructor call can be flexibly placed anywhere in the derived constructor. D can also have one constructor call another one:
class A { int a; int b; this() { a = 7; b = foo(); } this(int x) { this(); a = x; } }Members can also be initialized to constants before the constructor is ever called, so the above example is equivalently written as:
class A { int a = 7; int b; this() { b = foo(); } this(int x) { this(); a = x; } }
struct A x, y; ... x = y;it does not for struct comparisons. Hence, to compare two struct instances for equality:
#include <string.h> struct A x, y; inline bool operator==(const A& x, const A& y) { return (memcmp(&x, &y, sizeof(struct A)) == 0); } ... if (x == y) ...Note that the operator overload must be done for every struct needing to be compared, and the implementation of that overloaded operator is free of any language help with type checking. The C++ way has an additional problem in that just inspecting the (x == y) does not give a clue what is actually happening, you have to go and find the particular overloaded operator==() that applies to verify what it really does.
There's a nasty bug lurking in the memcmp() implementation of operator==(). The layout of a struct, due to alignment, can have 'holes' in it. C++ does not guarantee those holes are assigned any values, and so two different struct instances can have the same value for each member, but compare different because the holes contain different garbage.
To address this, the operator==() can be implemented to do a memberwise compare. Unfortunately, this is unreliable because (1) if a member is added to the struct definition one may forget to add it to operator==(), and (2) floating point nan values compare unequal even if their bit patterns match.
There just is no robust solution in C++.
A x, y; ... if (x == y) ...
#define HANDLE_INIT ((Handle)(-1)) typedef void *Handle; void foo(void *); void bar(Handle); Handle h = HANDLE_INIT; foo(h); // coding bug not caught bar(h); // okThe C++ solution is to create a dummy struct whose sole purpose is to get type checking and overloading on the new type.
#define HANDLE_INIT ((void *)(-1)) struct Handle { void *ptr; Handle() { ptr = HANDLE_INIT; } // default initializer Handle(int i) { ptr = (void *)i; } operator void*() { return ptr; } // conversion to underlying type }; void bar(Handle); Handle h; bar(h); h = func(); if (h != HANDLE_INIT) ...
typedef void *Handle = cast(void *)-1; void bar(Handle); Handle h; bar(h); h = func(); if (h != Handle.init) ...Note how a default initializer can be supplied for the typedef as a value of the underlying type.
class A { private: int a; public: int foo(B *j); friend class B; friend int abc(A *); }; class B { private: int b; public: int bar(A *j); friend class A; }; int A::foo(B *j) { return j->b; } int B::bar(A *j) { return j->a; } int abc(A *p) { return p->a; }
module X; class A { private: static int a; public: int foo(B j) { return j.b; } } class B { private: static int b; public: int bar(A j) { return j.a; } } int abc(A p) { return p.a; }The private attribute prevents other modules from accessing the members.
struct A { virtual int operator < (int i); virtual int operator <= (int i); virtual int operator > (int i); virtual int operator >= (int i); static int operator < (int i, A *a) { return a > i; } static int operator <= (int i, A *a) { return a >= i; } static int operator > (int i, A *a) { return a < i; } static int operator >= (int i, A *a) { return a <= i; } };A total of 8 functions are necessary, and all the latter 4 do is just rewrite the expression so the virtual functions can be used. Note the asymmetry between the virtual functions, which have (a < i) as the left operand, and the non-virtual static function necessary to handle (i < a) operations.
struct A { int cmp(int i); }The compiler automatically interprets all the <, <=, > and >= operators in terms of the cmp function, as well as handling the cases where the left operand is not an object reference.
Similar sensible rules hold for other operator overloads, making using operator overloading in D much less tedious and less error prone. Far less code needs to be written to accomplish the same effect.
namespace Foo { int x; } using Foo::x;
---- Module Foo.d ------ module Foo; int x; ---- Another module ---- import Foo; alias Foo.x x;Alias is a much more flexible than the single purpose using declaration. Alias can be used to rename symbols, refer to template members, refer to nested class types, etc.
class File { Handle *h; ~File() { h->release(); } };
The few RAII issues left are handled by auto classes. Auto classes get their destructors run when they go out of scope.
auto class File { Handle h; ~this() { h.release(); } } void test() { if (...) { auto File f = new File(); ... } // f.~this() gets run at closing brace, even if // scope was exited via a thrown exception }
A generic context pointer is also needed, represented here by void *p. The example here is of a trivial container class that holds an array of int's, and a user of that container that computes the maximum of those int's.
struct Collection { int array[10]; void apply(void *p, void (*fp)(void *, int)) { for (int i = 0; i < sizeof(array)/sizeof(array[0]); i++) fp(p, array[i]); } }; void comp_max(void *p, int i) { int *pmax = (int *)p; if (i > *pmax) *pmax = i; } void func(Collection *c) { int max = INT_MIN; c->apply(&max, comp_max); }The C++ way makes heavy use of pointers and casting. The casting is tedious, error prone, and loses all type safety.
class Collection { int[10] array; void apply(void delegate(int) fp) { for (int i = 0; i < array.length; i++) fp(array[i]); } } void func(Collection c) { int max = int.min; void comp_max(int i) { if (i > max) max = i; } c.apply(comp_max); }Pointers are eliminated, as well as casting and generic pointers. The D version is fully type safe. An alternate method in D makes use of function literals:
void func(Collection c) { int max = int.min; c.apply(delegate(int i) { if (i > max) max = i; } ); }eliminating the need to create irrelevant function names.