0

Given the following example code:

class Room {
    Room() : switch(*this) { }
    Lamp lamp;
    Switch switch;
    void TurnOn() { lamp.TurnOn(); }
}

class Switch {
    Switch(Room& room) : room(room) { }
    Room& room;
    void TurnOn() { room.lamp.TurnOn(); }
}

My understanding here is that the second TurnOn() involves an extra level of indirection, as we need to follow the reference to room. Is this correct? Will that extra indirection be removed if the call can be inlined (either via explicit inlining, or whole program optimization at linker level)? Or, put differently, could the TurnOn function in Switch be sped up by changing it to:

class Room {
    Lamp lamp;
    Switch switch;
    Room() : switch(*this,lamp) { }
    void TurnOn() { lamp.TurnOn(); }
}

class Switch {
    Room& room;
    Lamp& lamp;
    Switch(Room& room,Lamp& lamp) : room(room),lamp(lamp) { }
    void TurnOn() { lamp.TurnOn(); }
}

Or, more generally, if holding a reference to an object, is there a level of indirection less involved in accessing its members directly via a reference rather than via the reference and then the member?

Thanks

Cookie
  • 12,004
  • 13
  • 54
  • 83

2 Answers2

1

It might be faster (though not by much). However, both examples are incorrect in that they break encapsulation and violate the Law of Demeter. They require that either the Switch class or anyone who instantiates it have access to both the Room itself and the Lamp inside of it. Of course, we're also assuming that every Room has a Lamp, and that a Lamp can only ever exist within a Room...which means if those conditions ever change, there's two classes to change instead of just one.

The first example would be better written as

class Room {
  public:
    Room() : sw(*this) { }
    void TurnOn() { lamp.TurnOn(); }
  private:
    Lamp lamp;
    Switch sw;
};

class Switch {
  public:
    Switch(Room& room) : room(room) { }
    void TurnOn() { room.TurnOn(); }
  private:
    Room& room;
};

as then the Room is responsible for what gets turned on. Could be a lamp, could be a radio. Switch doesn't have to care anymore. This is more likely to be slower, but it's more maintainable.

If you want to require that Switch only turn on a Lamp, then

class Room {
  public:
    Room() : sw(lamp) { }
    void TurnOn() { lamp.TurnOn(); } // (semantics: who "turns on" a room?)
  private:
    Lamp lamp;
    Switch sw;
};

class Switch {
  public:
    Switch(Lamp& lamp) : lamp(lamp) { }
    void TurnOn() { lamp.TurnOn(); }
  private:
    Lamp& lamp;
};

This should be just as fast, without requiring that we break encapsulation.

cHao
  • 84,970
  • 20
  • 145
  • 172
0

Your second example will (probably) be no faster than the first example. In both cases, one reference needs to be resolved before TurnOn() can be invoked.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • It is, I think, implementation-defined. The compiler can simply use the original object, instead of the reference. – Nawaz Dec 05 '11 at 17:16
  • @Nawaz: If stuff can be inlined then yes, I agree; the compiler could do all sorts of optimization. But all other things being equal, neither of the OP's examples is more optimizable than the other. – Oliver Charlesworth Dec 05 '11 at 17:18
  • Shouldn't there be an advantage in calling TurnOn from the object that the reference points to? As in, runtime resolution shouldn't be necessary and that level of indirection should be resolvable at compile time? – Cookie Dec 05 '11 at 17:19
  • @Cookie: In both cases, a reference needs to be resolved (in the first, it's `Room &room`; in the second, it's `Lamp &lamp`). – Oliver Charlesworth Dec 05 '11 at 17:21
  • I see, that is indeed true. Quite badly posed question. So if either of those functions get called from within Room, they involve an extra indirection when not inlined, but with optimisation, that indirection might be removed again? – Cookie Dec 05 '11 at 17:32