Pass-by-Value Versus Pass-by-Reference

On this page:

Passing a Class by Reference

As we saw in Self-Referential Classes, classes naturally support pass-by-value semantics: passing a class transmits the data members of the class to the receiver. Any changes made to these data members by the receiver affect only the receiver's copy of the class; the data members of the sender's class are not affected by the changes made by the receiver.

In addition to passing a class by value, you can pass a class by reference.

Deprecated Feature

Passing class instances by reference - as proxies - is deprecated as of Ice 3.7. Skip this paragraph unless you need to communicate with old applications that rely on this feature.

For example:

Slice
module M
{
    class TimeOfDay
    {
        short hour;
        short minute;
        short second;
        string format();
    }

    interface Example
    {
        TimeOfDay* get();  // Note: returns a proxy!
    }
}

Note that the get operation returns a proxy to a TimeOfDay class and not a TimeOfDay instance itself. The semantics of this are as follows:

  • When the client receives a TimeOfDay proxy from the get call, it holds a proxy that differs in no way from an ordinary proxy for an interface.
  • The client can invoke operations via the proxy, but cannot access the data members. This is because proxies do not have the concept of data members, but represent interfaces: even though the TimeOfDay class has data members, only its operations can be accessed via a the proxy.

The net effect is that, in the preceding example, the server holds an instance of the TimeOfDay class. A proxy for that instance was passed to the client. The only thing the client can do with this proxy is to invoke the format operation. The implementation of that operation is provided by the server and, when the client invokes format, it sends an RPC message to the server just as it does when it invokes an operation on an interface. The implementation of the format operation is entirely up to the server. (Presumably, the server will use the data members of the TimeOfDay instance it holds to return a string containing the time to the client.)

The preceding example looks somewhat contrived for classes only. However, it makes perfect sense if classes implement interfaces: parts of your application can exchange class instances (and, therefore, state) by value, whereas other parts of the system can treat these instances as remote interfaces.

For example:

Slice
interface Time
{
    string format();
    // ...
}

class TimeOfDay implements Time
{
    short hour;
    short minute;
    short second;
}

interface I1 
{
     TimeOfDay get();           // Pass by value
     void put(TimeOfDay time);  // Pass by value
}

interface I2 
{
    Time* get();                // Pass by reference
}

In this example, clients dealing with interface I1 are aware of the TimeOfDay class and pass it by value whereas clients dealing with interface I2 deal only with the Time interface. However, the actual implementation of the Time interface in the server uses TimeOfDay instances.

Be careful when designing systems that use such mixed pass-by-value and pass-by-reference semantics. Unless you are clear about what parts of the system deal with the interface (pass by reference) aspects and the class (pass by value) aspects, you can end up with something that is more confusing than helpful.

Object vs Value

In non-local operations, you can use an Object or Value parameter to mean "accept any class or interface passed by value". Object and Value are synonymous in this context, and mean any value.

For example, interface I1 in the preceding example could be rewritten as:

Slice
interface UntypedI1 
{
     Value get();           // Pass by value
     void put(Value time);  // Pass by value
}

or

Slice
interface UntypedI1 
{
     Object get();           // Pass by value
     void put(Object time);  // Pass by value
}


In local operations, Object and Value are not interchangeable: Object designates the base class for all servants, while Value designates the base class for all mapped classes.

Finally for proxies, Object* always means any proxy, while Value* is never correct.

See Also