C++ Косвенность при доступе к членам из другого члена

Учитывая следующий пример кода:

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(); }
}

Насколько я понимаю, второй TurnOn() предполагает дополнительный уровень косвенности, так как нам нужно следовать ссылке на комнату. Это правильно? Будет ли эта дополнительная косвенность удалена, если вызов можно будет встроить (либо посредством явного встраивания, либо путем оптимизации всей программы на уровне компоновщика)? Или, другими словами, можно ли ускорить функцию TurnOn в Switch, изменив ее на:

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(); }
}

Или, в более общем смысле, если имеется ссылка на объект, существует ли уровень косвенности, менее связанный с доступом к его членам напрямую через ссылку, а не через ссылку, а затем член?

Спасибо


person Cookie    schedule 05.12.2011    source источник
comment
switch – это ключевое слово, его нельзя использовать как переменную.   -  person Nawaz    schedule 05.12.2011
comment
Не используйте переключатель (на самом деле вы не можете. Он зарезервирован для конструкции переключателя.)   -  person MGZero    schedule 05.12.2011
comment
Извините, неудачный выбор примера... Очевидно, я имел в виду выключатель света.   -  person Cookie    schedule 05.12.2011
comment
Если сомневаетесь, измерьте. Не искажайте свой код во имя производительности, если у вас нет конкретной причины.   -  person sbi    schedule 05.12.2011


Ответы (2)


Это может быть быстрее (хотя и ненамного). Однако оба примера неверны, поскольку нарушают инкапсуляцию и Закон Деметры. Они требуют, чтобы либо класс Switch, либо любой, кто его создает, имел доступ как к самому Room, так и к Lamp внутри него. Конечно, мы также предполагаем, что в каждой Комнате есть Лампа, и что Лампа может существовать только в Комнате... а это значит, что если эти условия когда-либо изменятся, нужно будет изменить два класса, а не один.

Первый пример лучше было бы записать как

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;
};

так как тогда Room отвечает за то, что включается. Может быть лампа, может быть радио. Switch теперь все равно. Это, скорее всего, будет медленнее, но более ремонтопригодно.

Если вы хотите, чтобы Switch включал только Lamp, то

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;
};

Это должно быть так же быстро, без необходимости нарушения инкапсуляции.

person cHao    schedule 05.12.2011

Ваш второй пример (вероятно) будет не быстрее первого примера. В обоих случаях необходимо разрешить одну ссылку, прежде чем можно будет вызвать TurnOn().

person Oliver Charlesworth    schedule 05.12.2011
comment
Это, я думаю, определяется реализацией. Компилятор может просто использовать исходный объект вместо ссылки. - person Nawaz; 05.12.2011
comment
@Nawaz: Если что-то можно встроить, то да, я согласен; компилятор может выполнять все виды оптимизации. Но при прочих равных ни один из примеров OP не оптимизируется лучше, чем другой. - person Oliver Charlesworth; 05.12.2011
comment
Разве не должно быть преимущество в вызове TurnOn из объекта, на который указывает ссылка? Например, разрешение во время выполнения не должно быть необходимым, и этот уровень косвенности должен разрешаться во время компиляции? - person Cookie; 05.12.2011
comment
@Cookie: в обоих случаях необходимо разрешить ссылку (в первом случае это Room &room, во втором — Lamp &lamp). - person Oliver Charlesworth; 05.12.2011
comment
Я вижу, это действительно так. Довольно некорректно поставленный вопрос. Таким образом, если какая-либо из этих функций вызывается из Room, они включают дополнительную косвенность, когда они не встроены, но с оптимизацией эта косвенность может быть снова удалена? - person Cookie; 05.12.2011