Виртуальные функции в конструкторах, почему языки отличаются?

Да, очень просто:

>>> s = "5+6"
>>> list(s)
['5', '+', '6']
12
задан Rann Lifshitz 19 May 2018 в 05:47
поделиться

6 ответов

Существует принципиальное различие в том, как языки определяют время жизни объекта. В Java и .NET элементы объекта являются нулем/пустым указателем, инициализированным, прежде чем любой конструктор будет выполнен и будет в этой точке, которую начинает объектное время жизни. Таким образом, при вводе конструктора, Вы уже получили инициализированный объект.

В C++ только начинается объектное время жизни, когда конструктор заканчивает (хотя членские переменные и базовые классы полностью создаются, прежде чем он запустится). Это объясняет поведение, когда виртуальные функции вызваны и также почему деструктор не выполняется, если существует исключение в теле конструктора.

Проблема с определением Java/.Net объектного времени жизни состоит в том, что более трудно удостовериться, что объект всегда встречает свой инвариант, не имея необходимость вставлять особые случаи для того, когда объект инициализируется, но конструктор не работал. Проблема с определением C++ состоит в том, что у Вас есть этот нечетный период, где объект находится в неопределенности и не полностью создан.

11
ответ дан 2 December 2019 в 07:23
поделиться

Оба пути могут привести к неожиданным результатам. Ваш лучший выбор не состоит в том, чтобы вызвать виртуальную функцию в Вашем конструкторе вообще.

C++ путем, я думаю, имеет больше смысла, но приводит к проблемам ожидания, когда кто-то рассматривает Ваш код. Если Вы знаете об этой ситуации, Вы не должны намеренно помещать свой код в эту ситуацию для пользы более поздней отладки.

7
ответ дан 2 December 2019 в 07:23
поделиться

Виртуальные функции в конструкторах, почему языки отличаются?

Поскольку нет никакого хорошего поведения. Я нахожу, что поведение C++ имеет больше смысла (так как c-скалистые-вершины базового класса называют первыми, это выдерживает обосновать, что они должны вызвать виртуальные функции базового класса - в конце концов, c-скалистая-вершина производного класса еще не работала, таким образом, это не могло настроить правильные предварительные условия для виртуальной функции производного класса).

Но иногда, где я хочу использовать виртуальные функции для инициализации состояния (таким образом, не имеет значения, что их называют с неинициализированным состоянием) поведение C#/Java более хорошо.

2
ответ дан 2 December 2019 в 07:23
поделиться

Я думаю, что C++ предлагает лучшую семантику с точки зрения наличия 'самого корректного' поведения... однако это - больше работы для компилятора, и код definitiely неинтуитивный кому-то читающему его позже.

С подходом.NET функция должна быть очень ограничена для не доверия любому состоянию производного объекта.

1
ответ дан 2 December 2019 в 07:23
поделиться

Delphi хорошо использует виртуальных конструкторов в платформе GUI VCL:

type
  TComponent = class
  public
    constructor Create(AOwner: TComponent); virtual; // virtual constructor
  end;

  TMyEdit = class(TComponent)
  public
    constructor Create(AOwner: TComponent); override; // override virtual constructor
  end;

  TMyButton = class(TComponent)
  public
    constructor Create(AOwner: TComponent); override; // override virtual constructor
  end;

  TComponentClass = class of TComponent;

function CreateAComponent(ComponentClass: TComponentClass; AOwner: TComponent): TComponent;
begin
  Result := ComponentClass.Create(AOwner);
end;

var
  MyEdit: TMyEdit;
  MyButton: TMyButton;
begin
  MyEdit := CreateAComponent(TMyEdit, Form) as TMyEdit;
  MyButton := CreateAComponent(TMyButton, Form) as TMyButton;
end;
0
ответ дан 2 December 2019 в 07:23
поделиться

Я нашел поведение C++ очень раздражающим. Вы не можете записать, что виртуальные функции к, например, возвращают желаемый размер объекта и имеют конструктора по умолчанию, инициализируют каждый объект. Например, было бы хорошо сделать:

BaseClass() { for (int i=0; i<virtualSize(); i++) initialize_stuff_for_index(i); }

С другой стороны преимущество поведения C++ состоит в том, что оно препятствует constuctors как вышеупомянутое от того, чтобы быть записанным.

Я не думаю проблема вызывающих методов, которые предполагают, что конструктор был закончен, хорошее оправдание за C++. Если бы это действительно было проблемой затем, то конструктору не разрешили бы назвать методы, так как та же проблема может относиться к методам за базовый класс.

Другая точка против C++ - то, что поведение намного менее эффективно. Хотя конструктор знает непосредственно, что это называет, vtab указатель должен быть изменен для каждого класса от основы до финала, потому что конструктор мог бы назвать другие методы, которые вызовут виртуальные функции. На основе моего опыта это тратит впустую намного больше времени, чем сохраняется путем совершения вызовов виртуальных функций в более эффективном конструкторе.

Намного более раздражающий то, что это также верно для деструкторов. Если Вы пишете виртуальную очистку () функция, и деструктор базового класса делает очистку (), это, конечно, не делает то, что Вы ожидаете.

Это и то, что деструкторы вызовов C++ на статических объектах на выходе имеют действительно pissed меня прочь в течение долгого времени.

0
ответ дан 2 December 2019 в 07:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: