Fog Creek Software
Discussion Board




Missing C++ feature?

THis one occurred to me recently, as I actually found I would have liked somrthing like it for a particular program.  Is this a missing C++ feature?

1. You can make a function which is specific to a specific instance of a class (non-static member)

2. You can make a function which is shared between all instances of a class (static)

3. You can make a function which is overriden by a derived class and is specific to a specifc instance of a class (virtual)

4. You can NOT make a function which is overriden by a derived class, but is shared between all instances of the class (static virtual?)


#4 may take some thinking about (as it is rare that you'd actually want something like it, and it runs against the typical C++ programming mindset), but I'd say it's logically hangs together as a possible construct (and no I don't really miss it)

S. Tanna
Sunday, June 08, 2003

I believe there is an Official Reason For It.

That kind of people that like to pretend OO is some kind of Number Theory have argued about this. They decided that "static virtual" and "static interface" (in Java) is Not Correct Object Oriented-ness.

Whatever the means. I still think I should be allowed to do it.

Google on it if you want to read yet another nerd freak out.

I believe that laziness is the root cause it's not possible in C++. From the implementations that I've seen it would be a pain in the butt to throw static virtual into the compiler.

I firmly believe that it's not possible in Java because, well as much as I love Java, I think the folks that run that show get off on saying things like "It's not correct OO"....

Matt
Sunday, June 08, 2003

I don't really think this makes sense... if you want to have static functions Base::f() and Derived::f() you can do that.  You can use the same name, but you will have to qualify the name with the class name when you call it.

A good exercise is, if you know C, to try simulating virtual functions and static functions.  Then it will become obvious why you can't have a static virtual function.

Basically virtual functions (and other non-static member functions) take an implicit this pointer, so they can access the member data.  Static functions do not.  They might as well not be part of any class.  It is basically "syntactic sugar" that you can group them with a class.  (OK as a side note I guess they have access to the static members of a class without Class::, but that is a detail).

I have tried to explain it better than that, but it all came down to explaining the details of the v-table, etc.  The whole point of virtual functions is polymorphism.  There is no polymorphism with static functions, because they don't operate on objects! 

B::static_function( int x ) doesn't operator on any object

B b;
b.nonstatic_function( int x ) can operator on the object b

Andy
Sunday, June 08, 2003

I've wanted this many times in the past.

"virtual static var" and "virtual static function"  are both missing.

E.g., suppose you want a "live instance count" for a class. With the C++ of today, every class needs to define it's own "static int live_count", which is incremented in the constructor, and decremented in the destructor.

If we had a "virtual static int live_count", then it would suffice to do the increase/decrease in the root object, and just have to re-define the var in every class which does not wish to be counted with its ancestor.

And the compiler could do a much better job if you could inquire class attributes through variables rather than through functions, e.g.

for (x = list.begin(); x != list.end(); ++x)
.    if (x->is_grokable) { grokable->append(x); }

Could generate much better code if is_grokable is a "virtual int" rather than a "virtual function". (assuming grokability is type dependent rather than instance dependent, of course).

It could be emulated, more or less efficiently. with non-virtual inline functions and base-class members. However, it does waste more memory, it does require a smarter compiler, and most importantly, it is not really interesting because all modern language are turing complete and thus anything can be emulated in any language.

Ori Berger
Sunday, June 08, 2003

Andy:

But note that

b.static_function(x) does work, and could be used to select the right non-virtual function even if b's class is not known at compile time.

There's no reason not to put pointers to static functions in the vtbl. you just have to call them without an instance when you do. (which is not a problem - if you got there through the vtbl, you already _have_ an instance, you just have to ignore it).

Ori Berger
Sunday, June 08, 2003

Whoops the above should be "operate" instead of "operator" obviously.

Actually as a shortcut to actually trying to implement the virtuals/statics in C, just study how the v-table works.  It is pretty simple.  EACH CLASS (as opposed to each object) has a v-table, which contains pointers to all the virtual functions.  EACH OBJECT has a pointer to the v-table.  So objects of type Base will have a pointer to the Base-v-table, and objects of type Derived will have a pointer to the Derived-v-table.  So at RUNTIME, the correct function Base::g or Derived::g can be invoked.

I think the confusion here is having a Java vs. C background.  C++ is built on C, so if you look at it from that perspective it all makes sense.  If you look at it from the Java perspective, it is all a huge mess.  You think, "C++ is a huge bunch of random rules, every rule has 10 exceptions".  When you think about it from the C level, you realize why those exceptions exist (and they cease to seem like exceptions).  When I was doing more Java programming and went over to C++, I was like "Why the heck is there even a virtual keyword? Why can't I just specify the same function name in class Base or class Derived and have it work polymorphically?"  When you understand the v-table you will understand this.

Andy
Sunday, June 08, 2003

See? Vtable shmee table. It's not in C++ because of laziness.

That's a perfectly fine reason.

However I just can't stand the Java reason.

Matt
Sunday, June 08, 2003

OK I oversimplified... you're right that b.static_function() works, which is weird, I wonder why.  I did read some google articles on it... and I haven't decided yet whether it cleans up the language or adds another special case.  I personally have not been wanting for static virtual functions.  But in any case you could just leave off the static.  Just declare it virtual, and don't access any members, and you just incur the negligible cost of passing this.  I guess it would be nicer though if you could declare it static so someone wouldn't later modify it to be non-static without thinking about it.

Still though something about b.static_function() seems pretty ugly, since it doesn't depend on b, only the type of b.  For that it seems like you want RTTI, which was probably added to solve a bunch of such problems.

for (x = list.begin(); x != list.end(); ++x)
.    if (x->is_grokable) { grokable->append(x); }

this example could be done with RTTI but yes it would be a lot more wasteful than a more obvious implementation of a static virtual data member.  I think the static virtual data member would be another feature that would be nice, but C++ already has so many damn features that adding one that would only be used once in awhile might make it even more complicated.

Interesting though, I never thought about it that carefully.  Does any other language have these features?  What about the "pure" OO languages?

Andy
Sunday, June 08, 2003

I'm not sure about statics in C++ but recently this same question was discussed on the Microsoft C# groups, and to my surprise the following works in C#:

class Animal {
    public static string Sound = "meow"; }

class Cat: Animal { }

class Cow: Animal {
    public new static string Sound = "moo"; }

-->

Animal.Sound == "meow"; // base class
Cat.Sound == "meow"; // static inheritance
Cow.Sound == "moo"; // static override

The "new" keyword tells the compiler that the naming conflict is intentional.  This example uses fields but static properties and methods (functions) work just the same.

Chris Nahr
Sunday, June 08, 2003

There's no way to do this in C++ or Java because it simply makes no sense. You would need another level of types above the current one, indicating which path down the inheritance tree your current static type represents.

I could have a class X with a static virtual ("Virtual()"), then inherit from that class A and class B overriding the virtual function in each. I call X::Virtual(). Which does it pick up? How does the compiler decide? What is the difference between the type X, the type X and the type X?

(If you want something like this so you can fill in the blanks of your base class in a derived class, use templates.

template<class T> class B {
  static void f() {T::virtual_f();}
};

struct C:public B<C> {
  static void virtual_f() {/*whatever*/return;}
};
)

Tom
Sunday, June 08, 2003

"I could have a class X with a static virtual ("Virtual()"), then inherit from that class A and class B overriding the virtual function in each. I call X::Virtual(). Which does it pick up?"

X obviously.  Ther is only one X.  I don't get your point...?

Chris Nahr
Sunday, June 08, 2003

The point is that you have 3 type X. One type X has the static function that is X's. One type X has the static virtual function that is A's. One type X has the static virtual function that is B's.

And you have the possibility that the static virtual that is B's might wish to call the static virtal that is X's. But, the static virtual that is B's _is_ the static virtual that is X's, because you can call it like "X::" -- otherwise, it wouldn't be much of a virtual.

A static function is bound to a type, not an object. You must be able to invoke a static function without using an object, just a type.

So, what is type X?

I'm not trying to be a dumbass :( but I don't see how static virtual can make any sense, unless the staticness means "shared between all objects" rather than bound to the type. It doesn't mean this in C++, and not in Java, and not in Python, so I don't see that static virtual makes any sense.

Tom
Sunday, June 08, 2003

Sorry. To clarify.

Firstly you don't get static in Python -- just class variables. But you can access these without needing an object. I'm using the mental shortcut of referring to this as a static object. Apologies to the redaer.

Secondly, when I say "you may invoke the virtual as X::" I of course mean "from outside code". You could probably fudge it so that inside the redefinition of a static virtual calls to the base class' function were considered to be jus that. But outside, you call "X::" and you potentially call a different function. (One of A or B, continuing my crappy example.) Therefore, the type X differs depending on... well, I'm not quite sure :)

My beef is with the idea that if you take "static" to mean what it does in C++/Java/Python, that a static virtual can make any sense. I don't see that it can. This is independent of language.

Tom
Sunday, June 08, 2003

1. The difference between a static and non-static member function, at the implementation level, is the non-static has a hidden this pointer, but the static doesn't

The non-static uses the this pointer to access the current object's data

The static, not having a this pointer, is the same on all instances of the object.

A. C++:

class A
{
public:
int a ;
void func_a( int x ) { a = x ; }
static void  func_b( void ) { printf("Hello") ; }
} ;


B. Equivalent to this C:

struct A
{
int a :
}

void func_a( A * this, int x )
{
this->a = x ;
}

void  func_b( void )
{
  printf("Hello") ;
}

C. Now Imagine some wierd C++ dialect which made the this pointer explicitly required, it would be something like (and while there ain't such a dialect, it does illustrate how the compiler is treating you code) :-

class A
{
public:
int a ;
void func_a( A * this, int x ) { this->a = x ; }
void func_b( void ) { printf("Hello") ; }
} ;

In this example func_b() has static removed - because by removing the this pointer, I have made the function static in effect anyway.



2. The difference between a virtual function and a non-virtual member function, at the implementation level, is the virtual function appears in the vtable

Now think about example 1C.  Whether the vtable is going to be one per instance or one per class (or you have two kinds of vtable for static and non-static members).  There is no reason why func_b() could not be included.

In other words func_b() (a static) could logically be virtual



3. Of course C++ does not allow it, which I think is justifiable but still a missing feature
(a) it's only a small benefit to allow this (rarely used)
(b) it's arguably not worth the amount of work to support this - justifiable laziness



4. Finally

class A
{
public:
int a ;
void func_a( int x ) { a = x ; }
static void  func_b( void ) { printf("Hello") ; }
} ;


Then
A.func_a() ; // works (i)
A::func_a() ; // doesn't work (ii)
B.func_b() ; // works (iii)
B::func_b() ; // works (iv)

Calling func_a() requires a instance of A and name scope.
(i) Works because the statement provides both
(ii) Doesn't work because no instance provided

Calling func_b() requires a name scope, but instance of A is NOT required (as func_b() has no this pointer)
(iii)  Works because we get an instance (we don't need it but does no harm), and name scope provided.
(iv) Provides name scope but no instance, which is sufficient

B.func_b() working is not a special case in the C++ rules, but instead simply a consequence of the name scoping rules. 

-- If you make a static member function which does some how get a pointer or reference to instances of the same class, that static member can access everything in that class including private data of the instances, etc.

S. Tanna
Sunday, June 08, 2003

Static virtual doesn't make sense. The point of virtual is to call a derived function through a pointer to the base class. Now imagine that you have a base class A with a static function. Imagine you have 2 other classes, B and C, both/each of which derive from A and which override A's static function. Imagine now that you call A::static_function() ... which of the two derived static functions would you want the compiler to call: B::static_function(), or C::static_function()?

Christopher Wells
Sunday, June 08, 2003

"The point is that you have 3 type X. One type X has the static function that is X's. One type X has the static virtual function that is A's. One type X has the static virtual function that is B's."

At the risk completely misremembering my C++ I'd say this is quite wrong.  You do NOT have three types X.  You always have ONE type X, no matter how many other types you derive from X.

When you make instances of X or a derived class then those instances can all be typed as "X" even though they might be A or B -- but they still have exactly one specific class associated with them internally, either X or A or B, and when you call statics through an instance variable (which C++ allows) the proper class's static member should be called polymorphically, as usual.

So I don't see the problem -- unless it's a problem with the details of the vtable implementation of C++.  There's no problem with inherited/overriden statics from an OOP viewpoint, however.

Chris Nahr
Sunday, June 08, 2003

"Imagine you have 2 other classes, B and C, both/each of which derive from A and which override A's static function. Imagine now that you call A::static_function() ... which of the two derived static functions would you want the compiler to call: B::static_function(), or C::static_function()?"

Ah, now I think I get what you people are saying.  Yes, you cannot call a static polymorphically using class names -- in other words, you can't get the actual type name to polymorph.

But as was mentioned WAY up this thread, this is really just a problem with a narrow ideological interpretation of OOP.  Obviously you'd want the A call to always call the A implementation, and not one of the derived classes' implementation.  But C++ has a call-static-through-instance feature that could actually polymorph statics, and C# has type objects that could refere either to a base type or a derived type.

Just to clarify -- does the C# example I gave above actually work in C++ or doesn't it?  It seems that we've been talking past each other and this was never clarified.

Chris Nahr
Sunday, June 08, 2003

It looks to me as if you are assuming that all aspects of a static function follow on from its lack of a thispointer. It is rather that a static function is one bound to a class rather than an instance. Its lack of a thispointer follows on from this.

So, it cannot be that a vtable for statics could be held in a particular object, because now the static function is associated not with a class but with an object. (You have a "sort of" "semi-static", that is inaccessible unless you have a class object. But this isn't static at all, really, because it's not associated with the class but with particular objects. Whilst this might sound superficially useful, I think in practice it will prove to be not so. You also introduce issues relating to the scope of the variables, a particularly important consideration in C++.)

It cannot be that a vtable for statics could be held in the class, because now you have another problem, alluded to above when I asked what the difference is between type X and type X. Consider: First, create class A. It has static virtual function f(). Now create class B, derived from class A. Now create class C, derived from class A. Now override f() in both.

Now... what is in the vtable for A?

What is the difference between type A and type A?

If there is a difference, they are not the same type, and static virtual is meaningless because you have not introduced any kind of polymorphism that is unsupported by the current C++. If there is no difference, you can override f() in just one derived class, you have not introduced anything that is unsupported by the current C++.

I hope that kind of explains it better. Anyway what is your original problem? :)

Tom
Sunday, June 08, 2003

Immediately after: The above was a reply to S Tanna's post, as it looks like others posted whilst I was composing.

Tom
Sunday, June 08, 2003

> does the C# example I gave above actually work in C++ or doesn't it?

Chris: your example works in C++. Not because the static is virtual and as is "overridden" in the derived class, but because a function with the same name in the derived class "hides" the same-named functions declared in base classes.

Christopher Wells
Sunday, June 08, 2003

I disagree with what you said.

Basically the  this pointer, or lack therefore IS the only real difference between a static and non static member.

The fact that you see a function as belonging to the class (static member) or an instance of the class (non-static member) is purely a MENTAL MODEL and CONVENTION of what is considered "good" oop. 

However it is NOT actually fundamental to how it works - in reality functions are actually free standing and figure out which instance they belong to thangs to the this pointer.


Additionally, there would no reason why there couldn't be static's in some kind of vtable, either one per class or one per instance (with duplicatio) or whatever

In Java, or SmallTalk, I understand the argument that they have a religious attitude about "good" oop, so not include this type of feature

In C++, I think the philosophy should be let the programmer do almost any darn thing he chooses. No religion - e.g. you can write C++ programs that are pure procedural, pure OOP, mix of two, any whole variety of other models.    Therefore the only excuse to leave this feature out of C++ in my view, is not religion about "good" oop, but laziness.  I don't consider laziness to be that bad  a reason though :-)

S. Tanna
Sunday, June 08, 2003

And that was a reply to Tom's reply to my reply.

S. Tanna
Sunday, June 08, 2003

So, let me sum up.

You'd have one set of behaviors for calling the static by class name (i.e., A.function()), and another set of behavrios for calling the statib by instance (i.e., anAInstance.function()). The former could not support polymorphism, because there's no object to polymorph off of; the latter could, because there is.

I think the confusion factor alone is probably reason enough to rule this out.

I didn't really see any positive example of the usefulness of this in the thread. Does someone have a real world problem where they're blocked, and this would help?

Brad Wilson (dotnetguy.techieswithcats.com)
Sunday, June 08, 2003

Brad - It is the same behaviour in both cases, and YES it can support polymorphism

The difference is whether the function deals with the specific to the instance data, or not

This is stupid example, but illustrates

class Shape
{
public:
virtual int GetSideCount() = 0 ;
} ;

class Triangle: public Shape
{
protected:
  POINT m_Vertex[3] ;
public:
virtual int GetSideCount() { return 3; }
} ;

class Square:  public Shape
{
protected:
  POINT m_Vertex[4] ;
public:
virtual int GetSideCount() { return 4; }
};



All instances of Triangle share a common GetSideCount regardless - and regardless of what a particular data any particular instance contains.  Likewise for square.  i.e. does not depend on instance data, so should be a static

If Triangle/Square was not derived from Shape, it would be common sense to mae these functions static.


Further more of course it would support polymorphism

Consider an array of shapes[]

for ( int x = 0 ; x < 10 ; x++ )
{
  cout << shape[x]->GetSideCount() ;
}

Code like this, you would expect to work, regardless of whether the GetSideCount deals in per-instance or per-class data.  The only point as the moment, you only have the choice of per-instance, regardless of the fact you're not using any per-instance data.



As for being a serious flaw. no  I'm not sure it is a huge flaw, in the example given above you are wasting a this pointer parameter being passed in each call to GetSideCount.  If you can live without that, for this example at least, it's easy to work round at the cost of some efficiency.

I think there maybe cases, where it matters a bit more perhaps from a structural point of view

S. Tanna
Sunday, June 08, 2003

<However it is NOT actually fundamental to how it works - >

Aargh, I'm being unclear again! What I was trying to get at is that the nature of a static function is that it is bound to a type. This idea, that is in C++ under the name of "static", is something you see in other languages too. It is not specific to C++. How this kind of thing works is, really, irrelevant --  you are proposing a feature that is unworkable based on the definition of this facility.

It would be unworkable in Java, unworkable in Python, unworkable in Smalltalk, and unworkable in Visual BASIC. C++ is a red herring.

<in reality functions are actually free standing and figure out which instance they belong to thangs to the this pointer.>

I think this is backwards (statement of fact, rather than implication :). I'm aware that functions, static or otherwise, are actually free standing. So, yes -- to talk of a function 'belonging' to a class or an instance is, in C++, fairly meaningless. (I'm familiar with how C++ generally works "under the hood", at least for compilers targeting Intel x86 family).

But it is not the function itself that does any kind of 'figuring out' -- a function is simply called, starts running, then finishes -- or so you hope? -- and returns. There has to be some mechanism to work out which function to call.

It is this mechanism that is the problem. For "static virtual", I don't see that it is possible to determine the function to call without needlessly constraining the situations in which it will work correctly, and without producing something that bears little relation to the concepts of "static" or "virtual", be they in C++ or any of the other OO languages I've used. [And I don't claim to have used them all, not by any means.]

You end up with a base class that is not a single class, for it is different depending which path through the tree you take. You end up with "static" objects that aren't. You end up with a feature that is fairly useless. Whichever way you try to create such a facility, you run into some kind of a problem.

It all boils down to that, for each type, this type exists "singly". It makes no sense to talk of multiple types that are of the same type. Each type simply "is". You don't get the chance to associate several different things with a type, depending on "something", which is what "virtual static" must ultimately involve. You can do this with objects, because the functions depend on the instance's vtable/method array/whatever. You can't do this with types, because you can't change something between one type and itself. (Because if it were another type...)

That last bit seems like nonsense now I read it again. However, like a blinding flash, I think I now see the point of the duck joke :)

Q: What is the difference between a duck?
A: One of its legs is both the same.

(I don't see how it can work -- but I am prepared to be proven wrong!! I hope you will forgive me the odd bit of bombast and/or rhetoric :-)

Tom
Sunday, June 08, 2003

Brad asked "Does someone have a real world problem where they're blocked, and this would help?"

I did. At first I thought this thread on static virtual was silly. Then I realized it would solve my problem of having a static classID variable so that I could tell what type something was from its base class.

But reading the thread solved my problem -- I will just replaced my dumb static ID with a virtual function that returns that Id and life is wonderful.

So, I had a problem where this would help and then I got around it and came up with something better thanks to this thread.

X. J. Scott
Sunday, June 08, 2003

The larger insight here is that my screwup was from thinking that "static members are appropriate for constants that apply to all members of the class", which is not true and was putting me in a blocked mindset. It's good to let go to these 'common sense teachings' when we get blocked.

X. J. Scott
Sunday, June 08, 2003

If I'm reading this correctly, "static virtual" as described here would only be useful if you already have an instance pointer -- p->StaticFunc() would resolve to Derived::StaticFunc() but Base::StaticFunc() would still resolve to Base::StaticFunc().  In other words, "static virtual" would be equivalent to "virtual" without a "this" pointer being passed as the first parameter.  Thus, the existing virtual function mechanism already adequately supports this as long as you don't mind the unnecessary "this" pointer being dragged along. 

Reasons someone might not want the "this" pointer being dragged along:
* Efficiency.  You don't want the overhead of passing a "this" pointer that you don't use. 
* Safety.  You don't want instance data to be accidently used (const will prevent writing but not reading). 
* Calling convention compatibility.  For example, it's common when using C++ with Win32 to declare a "static" member function to use as a WndProc. 

Of course, these are also reasons you might use a non-virtual "static" function.  The question is can anyone think of a circumstance where you would want these things combined with the "virtual" mechanism? 

SomeBody
Sunday, June 08, 2003

A proposal to allow this was submitted to the C++ board members in 1995 ( http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/1995/N0736.htm ) but it was rejected with no reason given ( http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/1996/N0904.pdf&e=6251 ). 

r1ch
Sunday, June 08, 2003

Geez reading over this thread again is really incoherent... : )  Doesn't look like anyone read my "conversion"...  I think we can now safely say that:

1) as I mentioned initially, static virtual can be replaced by just plain virtual now, with the overhead of passing this (and the drawback of safety)

2) it IS possible to put statics in the v-table and call them polymorphically through an instance, whether it is desirable is another story

3) a common use for static virtual would be to simulate RTTI, e.g. what if you wanted to print out the type of every object at runtime?  static virtual functions would be a natural candidate.

Andy
Sunday, June 08, 2003

The problem here is that C++ uses completley bogus names for object oriented concepts, and screws up those concepts at the same time.

In standard object-oriented terminology, classes have methods, not "member functions."  Methods are invoked by sending messages to objects.  Messages sent to instances are handled by instance methods, and messages sent to classes are handled by class methods.

In C++, instance methods are approximated by virtual member functions.  (I say "approximated" because C++ doesn't do full dynamic dispatch like Smalltalk or Objective-C, and therefore isn't a true object-oriented language; virtual function dispatch, which is still bound at compile time, is the closest it comes.)  So when you send a message to an instance, it's handled by a virtual member function; you can override these in subclasses etc. and it all works.

The flaw is that C++ approximates class methods with static member functions, rather than treating classes as objects themselves.  One of the things you can do in real object-oriented languages is assign a class to a variable, and then send it messages just as you would an instance.  In Objective-C, for instance, I can say "Class c = [NSArray class];" and then "NSArray *a = [[c alloc] init];".  In Smalltalk, I can say "c := OrderedCollection." and then "a := c new."  In C++, I can't do this, period.

This is a very powerful idiom.  I use it in an Objective-C framework I've developed for specifying custom classes of objects to be instantiated in a rule system in the data file used to specify the rules themselves.  You can't do this using the base C++ language, you have to build a bunch of your own infrastructure.  In Objective-C and Smalltalk (and, to a lesser extent, Java) the facility is completely general.

Chris Hanson
Tuesday, June 10, 2003

Chris, I'm not sure what you mean about virtual functions not being true dynamic dispatch.  They are not bound at compile time, as you say.  Virtual function dispatch require lookup in the virtual table at runtime to find the method to call

Mike McNertney
Tuesday, June 10, 2003

I don't know much about "pure" OOS languages, but I'm guessing he's talking about early binding vs. late binding (or maybe message interception).

Brad Wilson (dotnetguy.techieswithcats.com)
Tuesday, June 10, 2003

I'm talking about late binding.

Virtual functions *are* bound at compile time, just indirectly so.

A virtual function invocation:

  myObject->someFunction(argument);

compiles down to something like this:

  (*myObject->vtable[1][3])(myObject, argument);

Dynamic dispatch would look more like this:

  (*look_up_method(myObject, hash("someFunction")))(myObject, argument)

So in Objective-C:

  [myObject someSelector:argument];

becomes:

  objc_msgSend(myObject, @selector(someSelector:), argument);

Is this clearer?

The cool part is if classes are first class (sorry) objects themselves: You can do things like override class methods, so sending +alloc to a supposedly "abstract" base class will raise an exception but sending it to a subclass won't.  (This is part of how class clusters are implemented in OpenStep & Cocoa.)

Chris Hanson
Friday, June 13, 2003

*  Recent Topics

*  Fog Creek Home