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.
class Abc { public: void setProperty(int newproperty) { property = newproperty; } int getProperty() { return property; } private: int property; }; Abc a; a.setProperty(3); int x = a.getProperty();All this is quite a bit of typing, and it tends to make code unreadable by filling it with getProperty() and setProperty() calls.
class Abc { void property(int newproperty) { myprop = newproperty; } // set int property() { return myprop; } // get private: int myprop; }which is used as:
Abc a; a.property = 3; // equivalent to a.property(3) int x = a.property; // equivalent to int x = a.property()Thus, in D a property can be treated like it was a simple field name. A property can start out actually being a simple field name, but if later if becomes necessary to make getting and setting it function calls, no code needs to be modified other than the class definition. It obviates the wordy practice of defining get and set properties 'just in case' a derived class should need to override them. It's also a way to have interface classes, which do not have data fields, behave syntactically as if they did.
template<int n> class factorial { public: enum { result = n * factorial<n - 1>::result }; }; template<> class factorial<1> { public: enum { result = 1 }; }; void test() { printf("%d\n", factorial<4>::result); // prints 24 }
template factorial(int n) { enum { factorial = n* .factorial!(n-1) } } template factorial(int n : 1) { enum { factorial = 1 } } void test() { printf("%d\n", factorial!(4)); // prints 24 }