Thread overview | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
March 05, 2002 vpt initialized before constructor? | ||||
---|---|---|---|---|
| ||||
Hi, hm, if I got it right: - the vp-table is initialized, and the members are set to something static before the constructor is called - you can call funktions from within the construktor. These are dynamically linked (since the vpt exist). This means, that within any member-function, there could be 2 states: - The own constructor could be completed successfull - all is ok - The function is called from a constructor (or a function the constructor called...). This means, the member-variables are initialized with default values, given at compile time. Isn't this bad? - Either I assume the default values for members are alright and valid - so I need no constructor at all and can take this() {} out of language? - Or I assume, static linked default values are not enough, but members will be filled within the constructor - so I have to check the members in each member-function whether they already initialized or not. Are there a solution? Imi |
March 05, 2002 Re: vpt initialized before constructor? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Immanuel Scholz | What happens is a static initializer is prepared for each class. Memory is allocated and the static initializer is copied over it. Then, the constructor is called. This ensures that within the constructor the object is in a defined state, virtual functions can be called, and all members are initialized to the default value. For many (if not most) classes, this is sufficient and it is not even necessary to have a constructor. It guarantees no more bugs due to adding a member and forgetting to initialize it in one of the constructors. Constructors are only necessary if any dynamic initialization is needed. "Immanuel Scholz" <digital-mars@kutzsche.net> wrote in message news:a630s6$241$1@digitaldaemon.com... > Hi, > > hm, if I got it right: > > - the vp-table is initialized, and the members are set to something static > before the constructor is called > - you can call funktions from within the construktor. These are dynamically > linked (since the vpt exist). > > This means, that within any member-function, there could be 2 states: > > - The own constructor could be completed successfull - all is ok > - The function is called from a constructor (or a function the constructor > called...). This means, the > member-variables are initialized with default values, given at compile time. > > > Isn't this bad? > > - Either I assume the default values for members are alright and valid - so > I need no constructor at > all and can take this() {} out of language? > - Or I assume, static linked default values are not enough, but members will > be filled within > the constructor - so I have to check the members in each member-function > whether they already > initialized or not. > > Are there a solution? > > Imi > > |
March 05, 2002 Re: vpt initialized before constructor? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Immanuel Scholz | "Immanuel Scholz" <digital-mars@kutzsche.net> wrote in message news:a630s6$241$1@digitaldaemon.com... > Hi, > > hm, if I got it right: > > - the vp-table is initialized, and the members are set to something static before the constructor is called YES! > - you can call funktions from within the construktor. These are dynamically > linked (since the vpt exist). YES!!! > This means, that within any member-function, there could be 2 states: > > - The own constructor could be completed successfull - all is ok > - The function is called from a constructor (or a function the constructor > called...). This means, the > member-variables are initialized with default values, given at compile time. > > > Isn't this bad? No. You should design your interface and implementations in such a manner that things like that don't happen - or at least if they do, it's your responsibility to ensure proper work. There might be a function that doesn't rely on object variables, but rather changes them: class Foo { int bar; this(int n) { init(1); ... } this(float f) { init(2); ... void init(n) { bar = n * n; } } Definitely a stupid, meaningless example... yet it shows that everything might not be that bad. > - Either I assume the default values for members are alright and valid - so > I need no constructor at > all and can take this() {} out of language? Constructor has duties other than assigning values to members. It might perform special specialization routines. For example, the Window class might want to call CreateWindowEx in its constructor. > - Or I assume, static linked default values are not enough, but members will > be filled within > the constructor - so I have to check the members in each member-function > whether they already > initialized or not. Just don't call member functions that rely on class variables from the constructor. |
March 05, 2002 Re: vpt initialized before constructor? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pavel Minayev | "Pavel Minayev" <evilone@omen.ru> schrieb im Newsbeitrag news:a634lr$3qf$1@digitaldaemon.com... > "Immanuel Scholz" <digital-mars@kutzsche.net> wrote in message > > - Or I assume, static linked default values are not enough, but members > will > > be filled within > > the constructor - so I have to check the members in each member-function > > whether they already > > initialized or not. > > Just don't call member functions that rely on class variables from the constructor. HA! And here is the point I want to show. In C++ of course you can call to member-functions from within the constructor too. But since they are static linked, you may only be aware of functions, that your own constructor in your own class call. In D member functions may be called from constructors you didn't even know they exist, so you live with the bad feeling in your stomach, whether this function may be called before the own constructor finished? Example: class foo : public bar { int x = 0; this {super(); x = calculate_x_right();} int foobar() { // here you cannot be sure, that x contains a right value (returned from // calculate_x_right), because the class bar may define int foobar() and // call it within its constructor... // all you can do is check it (but not with an assertion, because it may // depend on runtime-things whether foobar is called in a superclass) if (x != 0) ... } The check for x != 0 stinks, but you can't really go without it, unless you check the whole hirachy from foo upwards and this may be impossible. Imi |
March 05, 2002 Re: vpt initialized before constructor? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | "Walter" <walter@digitalmars.com> schrieb im Newsbeitrag news:a634fp$3p4$1@digitaldaemon.com... > What happens is a static initializer is prepared for each class. Memory is allocated and the static initializer is copied over it. Then, the constructor is called. > > This ensures that within the constructor the object is in a defined state, virtual functions can be called, and all members are initialized to the default value. For many (if not most) classes, this is sufficient and it is > not even necessary to have a constructor. It guarantees no more bugs due to > adding a member and forgetting to initialize it in one of the constructors. > > Constructors are only necessary if any dynamic initialization is needed. This happens due you presume that a "defined state" is some static value. So you could consider to forbid the constructor to alter the member variables and only allow static pre-constructor-constructs? This means, the problem (stated best in the other posting to Pavel) vanished, but many coders will kill you for this ;) Imi |
March 05, 2002 Re: vpt initialized before constructor? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Immanuel Scholz | "Immanuel Scholz" <digital-mars@kutzsche.net> wrote in message news:a636cb$4fo$1@digitaldaemon.com... > This happens due you presume that a "defined state" is some static value. > So you could consider to forbid the constructor to alter the member > variables and only allow static pre-constructor-constructs? > This means, the problem (stated best in the other posting to Pavel) > vanished, but many coders will kill you for this ;) If you forbid constructor to modify member variables, it simply loses sense... moreover, it is easy overridden by: class Foo { int bar; this() { init(); } // I don't modify anything... init() { bar = 666; } // muhahahahahaha! =) } |
March 05, 2002 Re: vpt initialized before constructor? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Immanuel Scholz | "Immanuel Scholz" <digital-mars@kutzsche.net> wrote in message news:a635ug$49p$1@digitaldaemon.com... > HA! And here is the point I want to show. > In C++ of course you can call to member-functions from within the > constructor too. > But since they are static linked, you may only be aware of functions, that > your own > constructor in your own class call. > In D member functions may be called from constructors you didn't even know > they exist, so you live with the bad feeling in your stomach, whether this > function may be called before the own constructor finished? Functions always exist. They, however, might rely on the data that wasn't yet properly initialized. For example the constructor of Window might call a caption() method, which'd in turn call SetWindowText, passing hWnd - which is still 0. Yes, it is possible. But it is something YOU should control. It's a usual consistency-vs-usability tradeoff. C++ constructors are very inconvenient, especially when it is required to call the base constructor with different arguments depending on what you get. It usually turns out to be something like: class Foo: public Bar { Foo(int n): Bar(n == 0 ? 1 : n == 1 ? 5 : n == 2 ? 10 : ...) { ... } } In D it can be written much cleaner: class Foo: Bar { this(int n) { switch (n) { case 0: super(1); return; case 1: super(5); return; ... } } } While this method has some quirks that you've mentioned, it is (IMO) much more practical. As long as you don't do "bad" things, the code is way more readable and flexible. |
March 05, 2002 Re: vpt initialized before constructor? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pavel Minayev | "Pavel Minayev" <evilone@omen.ru> schrieb im Newsbeitrag news:a638a2$5ai$1@digitaldaemon.com... > Functions always exist. They, however, might rely on the data that wasn't yet properly initialized. For example the constructor of Window might call a caption() method, which'd in turn call SetWindowText, passing hWnd - > which is still 0. Yes, it is possible. But it is something YOU should control. If I control class Window (to recognize the fact) and the class that redefine caption() (to include the check to hWnd) all is ok. But since I even don't know Window calls to caption, I usually have to check all variables in all functions before assuming, they have a value, that they get in the constructor. So you get no guarantee in any of you member-functions, that one of the constructors finished correctly. And this is IMHO a big disadvantage compared to C++. I tend to layout my Classes, that initializing is done within the constructor, so I can depend on a fully initialized class (not only static copied one) and do not have to check for validness on start on each member function. Maybe this tends to code like: class bar { this {init();} void init () { /* do the real init-stuff here */ } bool is_valid() { /* checks if initialized */ } f1 () {if (!is_valid()) init(); ...} // this first line appear in each function f2 () {if (!is_valid()) init(); ...} // which depends on a valid constructor-run f3 () {if (!is_valid()) init(); ...} f4 () {if (!is_valid()) init(); ...} } And worse: You can't use something like in{} and assert(bar) for this, since it may depend on runtime-decisions whether bar is initialized on call to a member or not. And worst: I think the real problem is deeper, because not everyone sees the problem behind this and may blindly depends on the fact, that the constructor is already passed successfully. And you need some luck to find such a bug in short time. > It's a usual consistency-vs-usability tradeoff. C++ constructors are very inconvenient, especially when it is required to call the base constructor with different arguments depending on what you get. It usually turns out to be something like: > > class Foo: public Bar > { > Foo(int n): Bar(n == 0 ? 1 : n == 1 ? 5 : n == 2 ? 10 : ...) { ... } > } > > In D it can be written much cleaner: > > class Foo: Bar > { > this(int n) > { > switch (n) > { > case 0: super(1); return; > case 1: super(5); return; > ... > } > } > } This is a different issue? Or how it is connected to my problem? It is no problem to call the base classes constructor at any other time, as long as these base class Bar cannot call any member-function within Foo before Foo.init() is finished! But anyway: Did you ensure (maybe with an warning, that can be turned to an error) that the user did not simple forget calling the base-constructor? > While this method has some quirks that you've mentioned, it is (IMO) much more practical. As long as you don't do "bad" things, the code is way more readable and flexible. <GRHN!> I disagree! It is not me who has to do no "bad things". It is the writer of my base classes! They should not be able to call my functions before my constructor finish. And I cannot access the source of my base-classes (either because I cannot or won't or shouldn't or have such time). Maybe all functions a constructor call (and all functions this function calls...) has to be final and cannot overidden? This might be a solution. Imi |
March 05, 2002 Re: vpt initialized before constructor? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Immanuel Scholz | "Immanuel Scholz" <digital-mars@kutzsche.net> wrote in message news:a63a8a$68f$1@digitaldaemon.com... > If I control class Window (to recognize the fact) and the class that > redefine > caption() (to include the check to hWnd) all is ok. But since I even > don't know Window calls to caption, I usually have to check all variables > in all functions before assuming, they have a value, that they get in the > constructor. You've just described the problem resolved by the class invariant! > So you get no guarantee in any of you member-functions, that one of > the constructors finished correctly. And this is IMHO a big disadvantage > compared to C++. C++ has a terrible disadvantage in that there's no guarantee there's any value other than garbage in any member. I can't even count the number of times I've had a bug where I added a member and then forgot to initialize it in one of the many constructors for it. With D, you have: 1. all members are guaranteed to at least contain their static default values. 2. you can use preconditions on a function to guarantee specific values. 3. you can use class invariants to guarantee correct construction before public member functions get called. None of this is supported by C++. > class bar > { > this {init();} > void init () { /* do the real init-stuff here */ } > bool is_valid() { /* checks if initialized */ } > > f1 () {if (!is_valid()) init(); ...} // this first line appear in each > function > f2 () {if (!is_valid()) init(); ...} // which depends on a valid > constructor-run > f3 () {if (!is_valid()) init(); ...} > f4 () {if (!is_valid()) init(); ...} > } The "is_valid()" function is analogous to the D class invariant, except with D the class invariant call is automatically inserted (and inherited). > And worse: You can't use something like in{} and assert(bar) for this, since it may depend on runtime-decisions whether bar is initialized on call to a member or not. I don't see how. > And worst: I think the real problem is deeper, because not everyone sees the problem behind this and may blindly depends on the fact, that the constructor is already passed successfully. And you need some luck to find such a bug in short time. D has both more flexibility in constructor design and more inherent robustness. You can add initialization guarantees using the class invariant. |
March 05, 2002 Re: vpt initialized before constructor? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | "Walter" <walter@digitalmars.com> wrote in message news:a634fp$3p4$1@digitaldaemon.com... > What happens is a static initializer is prepared for each class. Memory is allocated and the static initializer is copied over it. Then, the constructor is called. > > This ensures that within the constructor the object is in a defined state, virtual functions can be called, and all members are initialized to the default value. For many (if not most) classes, this is sufficient and it is > not even necessary to have a constructor. It guarantees no more bugs due to > adding a member and forgetting to initialize it in one of the constructors. > > Constructors are only necessary if any dynamic initialization is needed. > > "Immanuel Scholz" <digital-mars@kutzsche.net> wrote in message news:a630s6$241$1@digitaldaemon.com... > > Hi, > > > > hm, if I got it right: > > > > - the vp-table is initialized, and the members are set to something static > > before the constructor is called > > - you can call funktions from within the construktor. These are > dynamically > > linked (since the vpt exist). > > > > This means, that within any member-function, there could be 2 states: > > > > - The own constructor could be completed successfull - all is ok - The function is called from a constructor (or a function the constructor > > called...). This means, the > > member-variables are initialized with default values, given at compile > time. > > > > > > Isn't this bad? > > > > - Either I assume the default values for members are alright and valid - > so > > I need no constructor at > > all and can take this() {} out of language? > > - Or I assume, static linked default values are not enough, but members > will > > be filled within > > the constructor - so I have to check the members in each member-function > > whether they already > > initialized or not. > > > > Are there a solution? > > > > Imi > > And even then it shouldn't be a problem. *You* make the constructor of your class, so you know which functions it calls. Only these functions may not assume a completed constructor on it's call. Or am I missing something? -- Stijn OddesE_XYZ@hotmail.com http://OddesE.cjb.net __________________________________________ Remove _XYZ from my address when replying by mail |
Copyright © 1999-2021 by the D Language Foundation