Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!usenet.ins.cwru.edu!mephisto!mcnc!rti!xyzzy!bigbird.rtp.dg.com!langley From: langley@bigbird.rtp.dg.com (Mark L Langley) Newsgroups: comp.lang.c++ Subject: C(O2)++ Message-ID: <97@xyzzy.UUCP> Date: 14 Feb 90 22:22:37 GMT Sender: usenet@xyzzy.UUCP Reply-To: langley@bigbird.rtp.dg.com (Mark L Langley) Organization: Data General Corporation, Research Triangle Park, NC Lines: 125 I enjoyed Peter Kriens CO2 article that was posted recently to this group. <406@wmt.uucp> In it, he proposed a clever way to solve, in C, a chronic problem that does indeed arise in C++. Namely, that the abstraction for data hiding does not hide anything from the compiler, consequently changes to private member definitions require recompilation of code depending only on public definitions. In fact, I would add to that a suggestion that the current idea of visibility is flawed, because a field is ambiguous if it collides with something that is invisible. Consider struct Base1 { public: int MyData; }; class Base2 { private: int MyData; }; class Derived : public Base1, public Base2 { }; foo() { Derived d; d.MyData; } This means that every time you change the private implementation of a class, you may affect descendant classes adversely. This is quite antithematic. But unlike Mr. Kriens, I do not favor throwing the baby out with the bathwater. With much less work, you can apply this basic idea in C++, at no additional run-time cost. (Note that he had to do all his member look-ups at run-time.) Further, Mr. Kriens' article overlooked some clear advantages that C++ holds over C. For example operator and function overloading, more rigorous type checking, and pass by reference. Furthermore, using only the CO2 techniques in C, you are working with only half the primitives that you get with C++! Namely, the class metaphor involves methods and instance variables. CO2 is a step back to flavors (with its getable-setable instance variables) because it only has methods. Let me show you how you can do CO2 more profitably in C++, using C++ features. Consider the following schematic for an object. An object, ship, for example, has ship.h, shipI.h and shipI.c. In this scheme you can completely hide the definitions/declarations of the private members by encapsulating them in their own stylized class (shipI). By using references you can allow read-write, read-only or write-only access through a variables interface, and completely hide the implementation. // ship.h struct ShipI; class Ship { private: ShipI *shipI; public: int &x, &y, &velocity; Ship(); void print(); void update(); }; // shipI.h class Ship; class ShipI { friend Ship; operator ShipI *(); // How to make a ShipI from a *ShipI ShipI(Ship *); // How to construct a ShipI // // write through instance variables int x, y, velocity; // // completely private instance variable int time; }; // shipI.c #include "ship.h" #include "shipI.h" #include Ship::Ship() : shipI(ShipI(0)), x(shipI->x), y(shipI->y), velocity(shipI->velocity) { x = 10; y = 20; velocity = 100; shipI->time = 1; } void Ship::print() { int factor = velocity * shipI->time; printf("x=%d y=%d velocity=%d\n", x*factor, y*factor, velocity); } void Ship::update() { shipI->time ++; } ShipI::ShipI(Ship *) { printf("Creating Ship(I)\n"); } ShipI::operator ShipI*() { return new ShipI(0); } The only drawback is that access to local class members from member functions must be named explicitly. That is, to access a private instance variable from ship (that is really in shipI) you must access it as shipI->name. (See Ship::update for an example of this) By the way, I just noticed that I put in an old version of ship, and the above has a (subtle?) bug in it. It calls the constructor for Ship twice every time it creates a ship. This is disastrous if there are derivative classes of Ship. Do you see why? Mark Langley langley@dg-rtp.dg.com The owl of Minerva flys only at dusk... - Hegal