|
|
 | | From: | igouy at yahoo.com | | Subject: | Re: Static vs dynamic | | Date: | 13 Jan 2005 13:42:01 -0800 |
|
|
 | I am profoundly ignorant of C++ which may explain my confusion with this example.
afaict we don't need an abstract class and concrete class, or virtual functions, for there to be a problem:
class B { public: void f(){}; };
int main() { B* b = new B(); void* vp = b; vp.f(); }
test.cpp(10) : error C2228: left of '.f' must have class/struct/union type
|
|
 | | From: | Robert C. Martin | | Subject: | Re: Static vs dynamic | | Date: | Sun, 16 Jan 2005 08:33:46 -0600 |
|
|
 | On 13 Jan 2005 13:42:01 -0800, igouy@yahoo.com wrote:
>I am profoundly ignorant of C++ which may explain my confusion with >this example. > >afaict we don't need an abstract class and concrete class, or virtual >functions, for there to be a problem: > >class B { >public: >void f(){}; >}; > >int main() { >B* b = new B(); >void* vp = b; >vp->f(); // I corrected this from vp.f(). My fault. >}
Quite correct. You don't need the abstracts and virtuals. The point can be made without them.
In the above, the line "vp->f()" is attempting to call the 'f' method through a typeless pointer. In c++ you aren't allowed to do that. In C++ every call to a method is checked. The compiler makes sure that the *type* of the pointer has the appropriate method declared.
For example, had we said: "b->f();", the compiler would have been quite happy. The pointer 'b' is of type "pointer to B", and B has an 'f' method declared in it.
A dynamically typed language, on the other hand, does not do this checking at compile time. It would happily compile the "vp-f();" line. At run time, when this statement was executed, the object pointed to by vp would be sent a message of the form "execute f". If the object had a method named 'f', it would execute it. If not, it would throw an exception.
----- Robert C. Martin (Uncle Bob) | email: unclebob@objectmentor.com Object Mentor Inc. | blog: www.butunclebob.com The Agile Transition Experts | web: www.objectmentor.com 800-338-6716
"The aim of science is not to open the door to infinite wisdom, but to set a limit to infinite error." -- Bertolt Brecht, Life of Galileo
|
|
 | | From: | The Ghost In The Machine | | Subject: | Re: Static vs dynamic | | Date: | Fri, 14 Jan 2005 00:01:49 GMT |
|
|
 | In comp.lang.java.advocacy, igouy@yahoo.com
wrote on 13 Jan 2005 13:42:01 -0800 <1105652521.667667.163960@z14g2000cwz.googlegroups.com>: > I am profoundly ignorant of C++ which may explain my confusion with > this example. > > afaict we don't need an abstract class and concrete class, or virtual > functions, for there to be a problem: > > class B { > public: > void f(){}; > }; > > int main() { > B* b = new B(); > void* vp = b; > vp.f(); > } > > test.cpp(10) : error C2228: left of '.f' must have class/struct/union > type >
void * pointers have no type information at all (beyond them being a pointer to something). This is true in both C and C++, though in C pointers have no methods to call anyway. (A fair number of old C programs used 'char *' as a synonym for 'void *'.)
You are also trying to apply a method to a *pointer*, rather than what it points to. The correct code is more along the lines of
B * b = new B(); b->f();
or
(*b).f();
since f is part of B, not of B*. In this usage, b is a pointer to a class, struct, or union.
There is the method pointer construct
typedef void (B::*mptrType)(void);
with attendant usages
mptrType mptr = &B::f; /* unlike C, the & is required */
(b->*mptr)(); ((*b).*mptr)();
but the construct is seldom used. This is *not* to be confused with the *function* pointer construct
void f() { ... } typedef void (*fptr)(void);
fptrType fptr = &f; (*fptr)();
C++ shows a bit of weirdness, as the construct
fptr();
is also allowed. I can't say offhand whether the usage
class B { public: static void f() { ... } };
fptr = &B::f; /* or B::f? */
is allowed or not.
C++ also allows "smart pointers". These are simply classes which overload three methods:
class APointer { public: operator [const] AThing *() [const] { ... } [const] AThing * operator ->() [const] { ... } [const] AThing & operator *() [const] { ... } };
(I use [const] here to indicate that one can either have a 'const' declaration here, or not, as one's needs indicate.)
These can also be templated, with the usual caveats:
template class APointer { ... };
Smart pointers are a useful tool although they cannot guard against certain mistakes such as "losing the loop" memory leak or the more basic null/bad pointer exception. They might help against certain initialization problems, but are strictly optional.
Java, for its part, has no construct for method pointers or smart pointers, but doesn't really need either because of introspection and garbage collection.
Briefly put, if one declares a class such as
public class B { public B() { ... } public void f() { ... } public int g(int x) { ... } }
in Java, one can do things like
B b = new B(); Class b_c = B.class; java.lang.reflect.Method m_f = b_c.getMethod("f", new Class[]{}); m_f.invoke(b, new Object[]{}); java.lang.reflect.Method m_g = b_c.getMethod("g", new Class[]{Integer.TYPE}); Integer r = (Integer) m_g.invoke(b, new Object[]{new Integer(1)});
where permissions allow. Note that Integer.TYPE != Integer.class; the former indicates the primitive type "int", the latter the class for the Integer Object. The same holds true for Boolean, Byte, Character, Double, Float, and Long. (String is an Object and has no primitive type as such. Void.class is probably only seen as a return value of Method.getReturnType(); there's no real point in instantiating it.)
Also, method.invoke() must return an Object, not a primitive, and accept an array of Objects[], hence the explicit boxing and unboxing in the call above. Java 5 might fix this, allowing for
Integer r = (Integer) m_g.invoke(b, new Object[]{1});
or maybe even
int r = (int) m_g.invoke(b, new Object[]{1});
but I don't have a copy thereof to test it.
As you can probably see, the Java method is clumsier to actually use, but far more flexible, as one can use *any* Object and *any* method signature, not merely a subclass of the given pointer's basetype and the given method signature.
I don't know C#'s syntax in this area, and C# has the additional capability of declaring of setter and getter methods, allowing for some interesting syntactic shorthand but is also AFAICT a debugging headache.
-- #191, ewill3@earthlink.net It's still legal to go .sigless.
|
|
|