2005/5/10

     
 

References

artefaktur

| References | Casting | Arrays | Import | instanceof | Package | Synchronization | Throwable | finally | Dangerous | Stack | String |

In Java and ACDK, you usually only handle objects via references.


Content of this chapter:

   Intro
   Basic Operations
     Casts
     Testing if Reference is valid
   Reference Counting
   Advanced Operations
     RObject::operator->
     RObject::operator*
     RObject::operator&
     RObject::_ref_this()
   Implementation notes about reference
   ExtReference
   API Overview for Smartpointers



 Intro

First a little example, in Java and in C++ using acdk.


// Java
boolean
isHello(String str)
{
  String hello = new String("Hello");
  return str.equalTo(hello);
}

// C++ / acdk
bool
isHello(RString str)
{
  RString hello = new String("Hello");
  return str->equalTo(hello);
}

Beside the difference between boolean and bool, the most significant difference is member access via '->' and the 'RString' identifier, which is used instead of String.

In Java, all instances of types derived from Object are handled as a Reference.
You can prove this with the following code:


// java
void foo(StringBuffer sb, boolean getNewBuffer)
{
  if (getNewBuffer == false)
    sb.append(" Kommer");
  else
    sb = new StringBuffer("artefaktur");
}

StringBuffer sb = new StringBuffer("Roger");
foo(sb, false); // Callee change contents of sb
System.out.println(sb);
foo(sb, false); // Callee change the reference
System.out.println(sb);

To achieve the same, ACDK uses a template, which wraps the underlying object.
The following statement is often found in the header of ACDK:

ACDK_DECL_CLASS(AClass);

In general, all references to classes should follow the convention R[Classname].

 Basic Operations

 Casts



// Java
void foo()
{
  String string = new String("Hallo");
  Object object = string; // OK
  string = (String)object; // OK, because, RObject contains a String
  object = new Object();
  try {
    string = (Object)object; // This will throw
  } catch (RBadCastException ex) {
    System.out.println("Oops, you cannot asign an Object to a String");
  }
}
The same in C++ using acdk:

// C++ / acdk
void foo()
{
  RString string = new String("Hallo");
  RObject object = string; // OK
  string = (RString)object; // OK, because, RObject contains a String
  object = new Object();
  try {
    string = (RString)object; // This will throw
  } catch (RBadCastException& ex) {
    System::out->println("Oops, you cannot asign an Object to a String");
  }
}

 Testing if Reference is valid


In Java, you can test if a reference to an object is valid in the following way:

// Java
bool isValid(Object o)
{
  if (o == null)
    return false;
  else
    return true;
}

Some system headers or other vendor libraries tend to define 'null' with a macro like this:
#define null 0
To avoid problems with name conflicts, the 'null' value in acdk is Nil.


// C++ / acdk
bool isValid(RObject o)
{
  if (o == Nil)
    return false;
  else
    return true;
}

To explicitely release a reference, you can do following:

// Java
  Object o = new Object;
  o = null; // release Object;
// C++ / acdk
  RObject o = new Object;
  o = Nil; // release Object;

 Reference Counting

The ACDK Objects RObject, RStringBuffer and so on, controls the lifetime of the objects. They count the number of references holding the underlying object. Therefore they call the methods:

// increment number of reference hold this object instance
Object::addRef();

// decrement number of reference hold this object instance
Object::releaseRef();

If no reference hold the object instance, the object can be freed.


In normal situation these methods should and must not be used directly.

See also:  Memory.

 Advanced Operations


Smart ACDK references support following basic operations:

     RObject::operator->

  • operator ->: access holded Object. If Object is 0, NullPointerException will be thrown.
    Sample:
    
      RStringBuffer sb = new StringBuffer("asdf");
      sb->append("asdf"); // accessing method with operator ->
      
      RObject o = Nil;
      o->hashCode(); // throws NullPointerException
    

     RObject::operator*

  • operator * : dereferencing Object. If Object is 0, NullPointerException will be thrown.
    Sample:
    
      RStringBuffer sb = new StringBuffer("asdf");
      StringBuffer& sb2 = *sb;
      sb.append("asdf");
    

     RObject::operator&

  • operator & : referencing Object. Sample:
    
      RStringBuffer sb = new StringBuffer("asdf");
      StringBuffer* sb2 = &sb;
      sb->append("asdf");
    
    The & operator will often also used as cast-operator to cast up in the class hierarchi:
    
      void foo(RWriter out)
      {
      }
      RBinaryWriter bout = getBinaryWriter();
      //foo(bout); will not compile, because cannot convert RBinaryWriter to RWriter
      foo(&bout); //will compile, because can convert BinaryWriter* to RWriter
    

     RObject::_ref_this()

  • _ref_this(): get the pointer to RObject.
    because you cannot use the & to get the address of an RObject, the method _ref_this() is provided.
    Sample:
    
      RBinaryWriter bout = getBinaryWriter();
      RBinaryWriter* boutptr =  bout._ref_this();
    
    You should really need this only in very special situations.

 Implementation notes about reference

RObject is a typedef to RefHolder<Object>. Different to many other smart pointers using reference counting this implementation has following advantages:
  • The reference counting is thread-safe. You can use references to the same object in different threads without any problem. The synchronization is done with the best performance the platform provides (for example spinlocks).
  • If an Object is allocated on stack, reference counting is not synchronized. It doesn't make any sence (or is even imposible to share a stack object reference within thread.
  • A pointer or value can be converted to smart reference and vice versa without any problem.
  • There is no explicit ownership of an referenced object.
  • Explicit casts are possible. See  Casting.
  • Unfortunatelly some C++ compiler has problems with templates, so downcasts has to be done with the &operator.
    See  Casting.
  • Side-casts are possible (A implements interface B and C You have a reference to B and want to cast it to C.)
  • There are reference template for normal classes - ACDK_DECL_CLASS(AClass), for interfaces - ACDK_DECL_INTERFACE(AInterface) and for exceptions - ACDK_DECL_THROWABLE(AThrowable) and arrays.

 ExtReference

Foreign C++ classes - which are not derived from acdk::lang::Object cannot used via an ACDK reference by default.
But you can use the class  ExtObjectVal and  ExtObjectPtr to wrapp an existing C++ class or struct with a ACDK-Object.

// a foreign struct
struct MyStruct
{
  int _ivar;
  MyStruct(int i) : _ivar(i) {}
  int getI() { return _ivar; }
};

// this macro declares an MyStructObject and RMyStructObject and corresponding array types
// which can be used as normal ACDK Object
ACDK_DECL_EXT_CLASS_VAL(MyStruct);

// ACDK relies on basis services, which is provided by 
// acdk::lang::Object
// To implement these service (like hashCode, toString, compareTo, equals)
// implement global functions:
int acdk_hashCode(INP(RMyStructObject) s) { return s->_ivar; }
::acdk::lang::RString acdk_toString(INP(RMyStructObject) s) 
{ return acdk::lang::String::valueOf(s->_ivar); }

// now you can use MyStruct in ACDK way:
 RMyStructObject iobj = new MyStructObject(42);
  int i = iobj->getI();
  testAssert(i == 42);
  RString s = iobj->toString(); // calls acdk_toString(INP(RMyStructObject) s)
  testAssert(s->equals("42") == true);
// Use arrays
  RMyStructObjectArray sa= new MyStructObjectArray(1);
  sa[0] = new MyStructObject(41);
  testAssert(sa->length() == 1);
  RMyStructObject t = new MyStructObject(42);
  sa->append(t);
  testAssert(sa[0]->getI() == 41);
  testAssert(sa[1]->getI() == 42);

 API Overview for Smartpointers


Please refer to  API Overview for Smartpointers.

Constructs(1 / 12) next >