www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D classes inherane. How it works.

reply kov_serg <kov_serg freemail.ru> writes:
Sorry, for silly question by could anyone give link where I could found how D
constructor and destructors works. The behavious is very nice but much
different from C++. 

import std.stdio;

class A {
	void init()  { writefln("A.init"); }
	void fn()    { writefln("A.fn"); }
	void flush() { writefln("A.flush"); }
	this()       { writefln("A");init(); }
	~this()      { flush();writefln("~A"); }
}

class B : A {
	void init()  { writefln("B.init"); }
	void fn()    { writefln("B.fn"); }
	void flush() { writefln("B.flush"); }
	this() { writefln("B"); }
	~this() { writefln("~B"); }
}

void main() {
	writefln("D1.0 main");
	A a=new B;
	a.fn();
	delete a;
}
/*

D1.0 main
	A
	B.init
	B
	B.fn
	~B
	B.flush
	~A

C++ version
	A
	A.init (no B vft yet)
	B
	B.fn
	A.flush (dtor overrides vft)
	~A

*/

ps: C++ version more strong but hardly usefull. D version looks much better for
me. But how it implemented or should be implemented. If ~B already kills his
resources?
Apr 08 2008
next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
The simple answer is:

Get rid of all your destructors. You don't need them.
Get rid of all your delete statements. You don't need them.

That's pretty much how you write code in D. (D has a garbage
collector. It takes care of cleanup for you).
Apr 08 2008
parent reply BCS <BCS pathlink.com> writes:
Janice Caron wrote:
 The simple answer is:
 
 Get rid of all your destructors. You don't need them.
 Get rid of all your delete statements. You don't need them.
 
 That's pretty much how you write code in D. (D has a garbage
 collector. It takes care of cleanup for you).

that only works in most cases where the object only owns memory resources. If it owns files, mutexes, database connections, stock options or Corvettes, your sunk.
Apr 08 2008
parent reply Robert Fraser <fraserofthenight gmail.com> writes:
BCS wrote:
 that only works in most cases where the object only owns memory 
 resources. If it owns files, mutexes, database connections, stock 
 options or Corvettes, your sunk.

Really? I have yet to write a finalize() method in Java. IMO, using close() methods for non-memory resources and letting the GC clean up the memory makes for less cognitive load than worrying about the order of destruction, etc., etc.
Apr 08 2008
parent reply BCS <ao pathlink.com> writes:
Reply to Robert,

 BCS wrote:
 
 that only works in most cases where the object only owns memory
 resources. If it owns files, mutexes, database connections, stock
 options or Corvettes, your sunk.
 

close() methods for non-memory resources and letting the GC clean up the memory makes for less cognitive load than worrying about the order of destruction, etc., etc.

My comment was in light of "let the GC handle it". Your suggestion amounts to manual memory management (s/memory/resource/). That is fine but to make it work you need to keep track of lifetimes which isn't letting the GC do it. Personally I'd think the way to handle this kind of thing would be to try really hard to make order of destruction irrelevant and let the GC call ~this().
Apr 08 2008
parent reply Robert Fraser <fraserofthenight gmail.com> writes:
BCS wrote:
 My comment was in light of "let the GC handle it". Your suggestion 
 amounts to manual memory management (s/memory/resource/). That is fine 
 but to make it work you need to keep track of lifetimes which isn't 
 letting the GC do it. Personally I'd think the way to handle this kind 
 of thing would be to try really hard to make order of destruction 
 irrelevant and let the GC call ~this().

Fair enough, but designing classes such that the order of destruction is irrelevant is no trivial task.
Apr 09 2008
parent Kevin Bealer <kevinbealer gmail.com> writes:
Robert Fraser Wrote:

 BCS wrote:
 My comment was in light of "let the GC handle it". Your suggestion 
 amounts to manual memory management (s/memory/resource/). That is fine 
 but to make it work you need to keep track of lifetimes which isn't 
 letting the GC do it. Personally I'd think the way to handle this kind 
 of thing would be to try really hard to make order of destruction 
 irrelevant and let the GC call ~this().

Fair enough, but designing classes such that the order of destruction is irrelevant is no trivial task.

If the relationship from A to B is clear, then one approach is to use a static map to store the B objects. This insures that the ordering is preserved. class B { }; class A { this() { data = new B; safety_[data] = 1; } ~this() { data.close(); data.cleanup(); data.flush(); data.etc(); safety_.remove(data); } private: B data; int[B] safety_; }; This delays the destruction of B for another GC cycle, but maybe that's not a big deal. If you know that only A uses B, then you can probably also delete B from A.~this if you like. Beware of cycles, this code wasn't compiled, always ski in control, etc. Kevin
Apr 09 2008
prev sibling next sibling parent reply Jason House <jason.james.house gmail.com> writes:
D uses virtual functions by default (and C++ does not).  Most of your code is
showing virtual function handling vs. non-virtual function handling.

I'm unclear on how flush is getting called at all.

kov_serg Wrote:

 Sorry, for silly question by could anyone give link where I could found how D
constructor and destructors works. The behavious is very nice but much
different from C++. 
 
 import std.stdio;
 
 class A {
 	void init()  { writefln("A.init"); }
 	void fn()    { writefln("A.fn"); }
 	void flush() { writefln("A.flush"); }
 	this()       { writefln("A");init(); }
 	~this()      { flush();writefln("~A"); }
 }
 
 class B : A {
 	void init()  { writefln("B.init"); }
 	void fn()    { writefln("B.fn"); }
 	void flush() { writefln("B.flush"); }
 	this() { writefln("B"); }
 	~this() { writefln("~B"); }
 }
 
 void main() {
 	writefln("D1.0 main");
 	A a=new B;
 	a.fn();
 	delete a;
 }
 /*
 
 D1.0 main
 	A
 	B.init
 	B
 	B.fn
 	~B
 	B.flush
 	~A
 
 C++ version
 	A
 	A.init (no B vft yet)
 	B
 	B.fn
 	A.flush (dtor overrides vft)
 	~A
 
 */
 
 ps: C++ version more strong but hardly usefull. D version looks much better
for me. But how it implemented or should be implemented. If ~B already kills
his resources?

Apr 08 2008
next sibling parent reply Gregor Richards <Richards codu.org> writes:
Every destructor in the hierarchy will be called. I don't recall if you 
can do something like ~super(), but the default is exactly as you see: 
bottommost destructor to topmost destructor. flush() in A is being 
called because ~this() in A calls it.

So, you have to make sure that B's destructor doesn't destroy any data 
that is in fact owned by its parent class, A. Why would you, though?

However, as Janice mentioned, destructors aren't really that useful in a 
GC'd language. They're only useful when your class is stapling itself to 
some lower-level resource, like malloc'd memory or fopen'd files. I 
don't think I've actually written a destructor in any D code. At all.

  - Gregor Richards


Jason House wrote:
 D uses virtual functions by default (and C++ does not).  Most of your code is
showing virtual function handling vs. non-virtual function handling.
 
 I'm unclear on how flush is getting called at all.
 
 kov_serg Wrote:
 
 Sorry, for silly question by could anyone give link where I could found how D
constructor and destructors works. The behavious is very nice but much
different from C++. 

 import std.stdio;

 class A {
 	void init()  { writefln("A.init"); }
 	void fn()    { writefln("A.fn"); }
 	void flush() { writefln("A.flush"); }
 	this()       { writefln("A");init(); }
 	~this()      { flush();writefln("~A"); }
 }

 class B : A {
 	void init()  { writefln("B.init"); }
 	void fn()    { writefln("B.fn"); }
 	void flush() { writefln("B.flush"); }
 	this() { writefln("B"); }
 	~this() { writefln("~B"); }
 }

 void main() {
 	writefln("D1.0 main");
 	A a=new B;
 	a.fn();
 	delete a;
 }
 /*

 D1.0 main
 	A
 	B.init
 	B
 	B.fn
 	~B
 	B.flush
 	~A

 C++ version
 	A
 	A.init (no B vft yet)
 	B
 	B.fn
 	A.flush (dtor overrides vft)
 	~A

 */

 ps: C++ version more strong but hardly usefull. D version looks much better
for me. But how it implemented or should be implemented. If ~B already kills
his resources?


Apr 08 2008
parent reply "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"Gregor Richards" <Richards codu.org> wrote in message 
news:ftga4f$1gbn$1 digitalmars.com...
 Every destructor in the hierarchy will be called. I don't recall if you 
 can do something like ~super(), but the default is exactly as you see: 
 bottommost destructor to topmost destructor. flush() in A is being called 
 because ~this() in A calls it.

 So, you have to make sure that B's destructor doesn't destroy any data 
 that is in fact owned by its parent class, A. Why would you, though?

But it appears B's flush is being called, not A's, and B's data is definately destroyed, because ~B was called before it. Likewise, B's init is called before B's ctor! I find it all very worrying. It's a good thing I avoid inheritance like the plague lately. L.
Apr 08 2008
next sibling parent reply Jason House <jason.james.house gmail.com> writes:
It should always be considered an error to call a virtual member function in a
destructor. Someone should make a bug report for this. I would if I was at a
computer and not typing with my thumb...

Lionello Lunesu Wrote:

 
 "Gregor Richards" <Richards codu.org> wrote in message 
 news:ftga4f$1gbn$1 digitalmars.com...
 Every destructor in the hierarchy will be called. I don't recall if you 
 can do something like ~super(), but the default is exactly as you see: 
 bottommost destructor to topmost destructor. flush() in A is being called 
 because ~this() in A calls it.

 So, you have to make sure that B's destructor doesn't destroy any data 
 that is in fact owned by its parent class, A. Why would you, though?

But it appears B's flush is being called, not A's, and B's data is definately destroyed, because ~B was called before it. Likewise, B's init is called before B's ctor! I find it all very worrying. It's a good thing I avoid inheritance like the plague lately. L.

Apr 08 2008
parent reply "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"Jason House" <jason.james.house gmail.com> wrote in message 
news:fthbok$vs8$1 digitalmars.com...
 It should always be considered an error to call a virtual member function 
 in a destructor. Someone should make a bug report for this. I would if I 
 was at a computer and not typing with my thumb...

I agree, but this would mean that only final, static and global functions are allowed in ctors/dtors? Would be interesting to add a warning for this, to see how many times it occurs in existing code. L.
Apr 09 2008
parent reply Jason House <jason.james.house gmail.com> writes:
It's also safe to call private and templated functions.

Private functions, by definition, can't be overloaded. Templated functions are
defined to be non-virtual.

Does D have final functions?

Lionello Lunesu Wrote:

 
 "Jason House" <jason.james.house gmail.com> wrote in message 
 news:fthbok$vs8$1 digitalmars.com...
 It should always be considered an error to call a virtual member function 
 in a destructor. Someone should make a bug report for this. I would if I 
 was at a computer and not typing with my thumb...

I agree, but this would mean that only final, static and global functions are allowed in ctors/dtors? Would be interesting to add a warning for this, to see how many times it occurs in existing code. L.

Apr 10 2008
next sibling parent Jason House <jason.james.house gmail.com> writes:
Janice Caron Wrote:

 On 10/04/2008, Jason House <jason.james.house gmail.com> wrote:
  Private functions, by definition, can't be overloaded.

I suspect you mean "overridden".

Yes, sorry.
Apr 10 2008
prev sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
Jason House wrote:
 Does D have final functions?

Indeed. Using the "final" storage class.
Apr 10 2008
prev sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Lionello Lunesu wrote:
  It's a good thing I avoid inheritance like
 the plague lately.

Wouldn't it be better to avoid destructors like the plague instead?
Apr 08 2008
parent "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"Robert Fraser" <fraserofthenight gmail.com> wrote in message 
news:fthlbc$1fro$2 digitalmars.com...
 Lionello Lunesu wrote:
  It's a good thing I avoid inheritance like
 the plague lately.

Wouldn't it be better to avoid destructors like the plague instead?

I think there are more problems with inheritance than these "virtual calls in ctor/dtor" : ) I used to be so fond of OOP and its inheritance, but lately I find myself preferring composition and free-functions to "class frameworks". L.
Apr 08 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 10/04/2008, Jason House <jason.james.house gmail.com> wrote:
  Private functions, by definition, can't be overloaded.

I suspect you mean "overridden".
Apr 10 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 08/04/2008, BCS <BCS pathlink.com> wrote:
  that only works in most cases where the object only owns memory resources.
 If it owns files, mutexes, database connections, stock options or Corvettes,
 your sunk.

Yeah, yeah - I know - but kov_serg is a beginner, so I figured, start with the easy. Just get the hang of the marvellous thing that is the garbage collector, and when the day comes when you say "Right then - how do I close I file?", that's when you want to start needing to know about destructors.
Apr 08 2008