пятница, 22 августа 2014 г.

Коротко. Об "обратной устойчивости"

Начну "издалека".

С C++. Где оператора with - нет (и слава богу - не будет).

( Зато есть "блочные переменные" и конструкции вида int & X = MyClass.MyField.MySubField; )

Пусть есть код:

class A
{
 private:
  int X; // - это приватный член класса A
};//A

int X = 0; // - это глобальная переменная

class B : A
{
 void Dummy ()
 {
  X = 123; // - тут компилятор ОТРУГАЕТСЯ, скажет - "есть приватный член, 
           //   который может "затенять" глобальную переменную".
 };
};//B

-- в итоге код НЕ СКОМПИЛИРУЕТСЯ.

А как скомпилируется?

А вот так:

class A
{
 private:
  int X; // - это приватный член класса A
};//A

int X = 0; // - это глобальная переменная

class B : A
{
 void Dummy ()
 {
  ::X = 123; // - тут компилятор ругаться не будет, так как "поймёт", 
             //   что X - это ГЛОБАЛЬНАЯ переменная и ТОЛЬКО ОНА
 };
};//B

-- и эта стратегия компилятора - ПРАВИЛЬНАЯ.

Ведь если бы код компилировался, то что бы мы бы могли получить?

А вот что:

class A
{
 protected:
  int X; // - это "защищённый" член класса A
};//A

int X = 0; // - это глобальная переменная

class B : A
{
 void Dummy ()
 {
  X = 123; // - ОБА НА! тут X - "вдруг" стал членом класса A, 
           //   а не глобальной переменной
 };
};//B

-- и мы получаем "неожиданные результаты".

Но! Компилятор так не делает.

Это и называется "обратная устойчивость".

А вот оператор with в Delphi "обратной устойчивостью" не обладает. А жаль.

Поясню:

type
 TA = class
  public
   Caption : String;
 end;//TA

 TB = class
  public
   Caption : String;
   A : TA;
 end;//TB
...
procedure TB.SomeProc;
begin
 with A do
  Caption := '123'; // - это присвоится в Self.A.Caption 
end;

-- а теперь так:

Unit uA;
...
type
 TA = class
  private
   Caption : String;
 end;//TA
...
Unit uB;
...
 TB = class
  public
   Caption : String;
   A : TA;
 end;//TB
...
procedure TB.SomeProc;
begin
 with A do
  Caption := '123'; // - это присвоится в Self.Caption 
end;

-- скажем так - ОГО!

Ну и...

На C++ как можно было бы написать?

void TB::SomeProc ()
{
 ...
 {
  ...
  std::string & vCap = A.Caption;
  vCap = '123';
  ...
 }
 ...
}

-- понятно почему я написал про "переменные блока" и &? И почему "в этом контексте" языку C++ оператор with и не нужен.

7 комментариев:

  1. > понятно почему я написал про "переменные блока" и &?
    Не совсем. =(

    ОтветитьУдалить
    Ответы
    1. "Не совсем"

      int & X = A->B.->C->D->E;

      ...
      X = 1234;
      ....
      Y = X;
      ....
      Z = X;

      Удалить
    2. Вот теперь вообще ничего не понятно :). Что за Y и Z?

      Удалить
    3. Что именно непонятно?

      Y и Z - это "какие-то значения" совместимые с int & X.

      Удалить
    4. На Delphi можно написать:

      with A.B.C.D do
      begin
      ...
      x1 := SomeValue1;
      x2 := SomeValue2;
      end;

      А на C++:

      {
      SomeData & X = A.B.C.D;
      ...
      X.x1 := SomeValue1;
      X.x2 := SomeValue2;
      }

      -- так понятно?

      Удалить
  2. а если в первом примере class A вынести в отдельный модуль?

    ОтветитьУдалить
    Ответы
    1. "а если в первом примере class A вынести в отдельный модуль?"
      -- попробуете сами себе ответить на этот вопрос. Я лишь "задал вектор".

      Удалить