See what's going on with flipcode!




This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

 

  Information Hiding Tricks in C++
  Submitted by



As most object-oriented programmers know, information hiding is one of the most important things in OOP. It also causes problems, because there is cases when some other classes than deviced classes should have access to the hidden information. We still don't want this information be available through public interface and declaring friend classes isn't nice option either, because it strings some nasty dependencies between components. So what can we do? Here I present two of tricks that can be used, but should be used with caution:

1) If classes share same base class and you need to access protected base class members of a cross-class. Example follows:
   class Base
   {
   protected:
     void foo() {}
   };

class A: public Base {};

class B: public Base { public: void bar(A &a_) {a_.foo();} };


The following piece of code won't compile, because access to protected interface of a cross-class isn't allowed. What you can do, however, is that you cross-cast the passed argument to the type of the class where from you try to access the protected interface:

   class B: public Base
   {
   public:
     void bar(A &a_) {((B&)a_).foo();}
   };

Example of the usage: int main(void) { A a; B b; b.bar(a); return 0; }






Naturally there are risks in cross-casting as well as down-casting whereto this trick can also be applied on. For instance if you change the inheritance scheme of a cross-class (for instance change inheritance of A from Base to Base2), this would play havoc on your code if you forget to change the interface to match the new purpose. Also if you accidently call other than base class members after the typecasting it will cause damage. I faced need for this trick when I designed state handling framework for my 3D engine. Each different state as well as state controller class are derived from StateBase class. StateBase-class has derived by using protected inheritance from ListItem-class which implements implicit list accessors and mutators. Protected inheritance is used because I don't want to expose state list to consumers and prevent them writing code which relies that states are actually present in a list. Still, access to the list is required from the state controller and different states.



2) Another useful trick according information hiding I found when applying object factory pattern. In my 3D engine models (meshes) are created from vertex array and they need some low level access to the vertex array, which I don't want to expose to consumers (for instance querying IDirect3DDevice7 interface in D3D implementation). These two classes got no relations in inheritance scheme whatfor I can't use the first trick. However, because vertex array takes care of model creation, I can pass abstract access base class, which publicly declares required low level access and use private inheritance to inherit vertex array from it. The access interface is implemented in private section of vertex array ensuring that no other classes than created models can access it. By reguesting access base interface in model constructors I also prevent explicit instantiation of model classes. And example follows:

   class AccessBase
   {
   public:
     virtual void foo()=0;

protected: ~AccessBase() {}

private: void operator=(const AccessBase&); // no implementation };

class A { public: A(AccessBase &access_) {m_access=&access_;} void bar() {m_access-foo();}

private: A(const A&); // no implementation void operator=(const A&); // no implementation AccessBase *m_access; };

class Factory: private AccessBase { public: A *create() {return new A(*this);}

private: void foo() {} };

Example of the usage: int main(void) { Factory fact; A *a=fact.create(); delete a; return 0; }




If we look for drawback of this approach, it's obvious that we need to declare extra interface class which allows the access. If you wonder what are these "// no implementation" lines, they are just added to prohibit incorrect use of the interface allowed by auto generation of those members.


The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

 

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.