Treating classes as first-class objects

2019-06-21 08:59发布

问题:

I was reading the GoF book and in the beginning of the prototype section I read this:

This benefit applies primarily to languages like C++ that don't treat classes as first class objects.

I've never used C++ but I do have a pretty good understanding of OO programming, yet, this doesn't really make any sense to me. Can anyone out there elaborate on this (I have used\use: C, Python, Java, SQL if that helps.)

回答1:

For a class to be a first class object, the language needs to support doing things like allowing functions to take classes (not instances) as parameters, be able to hold classes in containers, and be able to return classes from functions.

For an example of a language with first class classes, consider Java. Any object is an instance of its class. That class is itself an instance of java.lang.Class.



回答2:

For everybody else, heres the full quote:

"Reduced subclassing. Factory Method (107) often produces a hierarchy of Creator classes that parallels the product class hierarchy. The Prototype pattern lets you clone a prototype instead of asking a factory method to make a new object. Hence you don't need a Creator class hierarchy at all. This benefit applies primarily to languages like C++ that don't treat classes as first-class objects. Languages that do, like Smalltalk and Objective C, derive less benefit, since you can always use a class object as a creator. Class objects already act like prototypes in these languages." - GoF, page 120.

As Steve puts it,

I found it subtle in so much as one might have understood it as implying that /instances/ of classes are not treated a first class objects in C++. If the same words used by GoF appeared in a less formal setting, they may well have intended /instances/ rather than classes. The distinction may not seem subtle to /you/. /I/, however, did have to give it some thought.

I do believe the distinction is important. If I'm not mistaken, there is no requirement than a compiled C++ program preserve any artifact by which the class from which an object is created could be reconstructed. IOW, to use Java terminology, there is no /Class/ object.



回答3:

In Java, every class is an object in and of itself, derived from java.lang.Class, that lets you access information about that class, its methods etc. from within the program. C++ isn't like that; classes (as opposed to objects thereof) aren't really accessible at runtime. There's a facility called RTTI (Run-time Type Information) that lets you do some things along those lines, but it's pretty limited and I believe has performance costs.



回答4:

You've used python, which is a language with first-class classes. You can pass a class to a function, store it in a list, etc. In the example below, the function new_instance() returns a new instance of the class it is passed.

class Klass1:
    pass

class Klass2:
    pass

def new_instance(k):
    return k()

instance_k1 = new_instance(Klass1)
instance_k2 = new_instance(Klass2)

print type(instance_k1), instance_k1.__class__
print type(instance_k2), instance_k2.__class__


回答5:

C# and Java programs can be aware of their own classes because both .NET and Java runtimes provide reflection, which, in general, lets a program have information about its own structure (in both .NET and Java, this structure happens to be in terms of classes).

There's no way you can afford reflection without relying upon a runtime environment, because a program cannot be self-aware by itself*. But if the execution of your program is managed by a runtime, then the program can have information about itself from the runtime. Since C++ is compiled to native, unmanaged code, there's no way you can afford reflection in C++**.

...

* Well, there's no reason why a program couldn't read its own machine code and "try to make conclusions" about itself. But I think that's something nobody would like to do.

** Not strictly accurate. Using horrible macro-based hacks, you can achieve something similar to reflection as long as your class hierarchy has a single root. MFC is an example of this.



回答6:

Template metaprogramming has offered C++ more ways to play with classes, but to be honest I don't think the current system allows the full range of operations people may want to do (mainly, there is no standard way to discover all the methods available to a class or object). That's not an oversight, it is by design.