Могу ли я выполнить некоторые инициализации перед `Inherited Create`?

Я хочу создать и запустить поток, все с одной командной строкой TClientCopyThread.Create(...). Для этого я должен создать поток с Suspended = False, чтобы он мог работать немедленно. Я знаю, что когда я пишу constructor нового объекта, в первую очередь я должен вызвать inherited Create, чтобы экземпляр объекта был создан, а затем уже выполнять свои инициализации. Но здесь, если я вызову inherited, поток запустится без инициализированных параметров. Пытаюсь вызвать inherited последним и вроде работает (никаких нарушений прав доступа не получаю), но точно не знаю, совпадение это или нет.

  TClientCopyThread = class(TThread)
  private
    OwnGUID: String;
    SrcPath, DestPath: String;
    Files: TFileNames;
    RemoveIt: Boolean;
  protected
    procedure Execute; override;
  public
    constructor Create(const GUID, ASrcPath, ADestPath: String;
     const FileNames: TFileNames; RemoveSrc: Boolean);
  end;

constructor TClientCopyThread.Create(const GUID, ASrcPath, ADestPath: String;
 const FileNames: TFileNames; RemoveSrc: Boolean);
var I: Integer;
begin
 SrcPath:=  Copy(ASrcPath, 1, Length(ASrcPath));
 DestPath:= Copy(ADestPath, 1, Length(ADestPath));
 SetLength(Files, Length(FileNames));
 for I:= 0 to High(Files) do
  Files[I]:= Copy(FileNames[I], 1, Length(FileNames[I]));
 RemoveIt:= RemoveSrc;
 FreeOnTerminate:= True;
 inherited Create;
end;

person Marus Nebunu    schedule 18.09.2020    source источник
comment
Хорошее место для запуска потока — AfterConstruction, где все конструкторы были выполнены и экземпляр должен быть стабильным.   -  person Uwe Raabe    schedule 19.09.2020
comment
@J... Я тоже об этом думал, но получаю сообщение об ошибке, если пытаюсь Start нить из конструктора. / Хорошо, так что память для моего нового объекта уже выделена, когда я вхожу в конструктор?   -  person Marus Nebunu    schedule 19.09.2020
comment
@UweRaabe TThread уже делает точно это, когда CreateSuspended=False   -  person Remy Lebeau    schedule 19.09.2020
comment
@MarusNebunu TThread.Start() вызывает исключение EThread, если объект TThread был создан с помощью CreateSuspended=False, или если поток уже завершил выполнение, или если Start() уже был вызван, или если указатель TThread был получен при вызове TThread.CurrentThread в потоке, отличном от TThread. Таким образом, должно быть совершенно безопасно вызывать Start() внутри производного конструктора при использовании inherited Create(True). Если это не работает, то это ошибка, которую необходимо исправить.   -  person Remy Lebeau    schedule 19.09.2020


Ответы (1)


Чтобы ответить на ваш конкретный вопрос - ДА, вы можете вызвать inherited Create в любой момент во время производного конструктора. НЕ обязательно быть первым оператором (то же самое с inherited Destroy в качестве последнего оператора в деструкторе). Память для объекта класса уже полностью выделена до вызова каких-либо конструкторов, поэтому перед вызовом конструктора inherited безопасно инициализировать члены производного класса. Однако при доступе к членам базового класса из производного конструктора вы должны сначала вызвать конструктор inherited, чтобы инициализировать их, прежде чем обращаться к ним.

При этом ваше понимание того, как работает конструктор TThread, просто неверно. Начиная с Delphi 6 и более поздних версий, конструктор TThread базового класса всегда создает базовый поток ОС в приостановленном режиме, а затем возобновляет поток в TThread.AfterConstruction() после полного выхода всех конструкторов, если Объект TThread построен с помощью CreateSuspended=False. Таким образом, ваше утверждение о том, что вызов inherited Create с CreateSuspended=False запустит поток, работающий немедленно, НЕ соответствует действительности с 2001 года, когда был выпущен Delphi 6. Базовый поток ОС НЕ запустится до тех пор, пока после не завершится ваш конструктор TClientCopyThread.Create(). Таким образом, ваш метод Execute() никогда не будет воздействовать на неинициализированные элементы, независимо от того, как вы установите CreateSuspended.

То, что вы описываете (поток, запущенный до инициализации элементов), было ошибкой в Delphi 5 и более ранних версиях, которая была исправлена ​​в Delphi 6.

person Remy Lebeau    schedule 18.09.2020
comment
Это справедливо только в том случае, если родительский класс не создает никаких внутренних/вложенных объектов/классов в своем собственном конструкторе. Если он не вызывает inherited Create достаточно быстро, это будет означать, что вы можете попытаться получить доступ к этим объектам из конструктора производного класса до того, как они будут должным образом созданы, что затем вызовет нарушение прав доступа. Так что да, хотя это верно для класса TThread, поскольку у него нет никаких внутренних объектов, многие не верны для других классов. - person SilverWarior; 21.09.2020
comment
@SilverWarior, очевидно, если конструктор производного класса хочет получить доступ к членам базового класса, ему необходимо сначала вызвать конструктор inherited для инициализации этих членов. Но этот вызов не обязательно должен быть первым оператором в производном конструкторе нежелаемого, хотя это наиболее распространенное использование. Я обновил свой ответ. - person Remy Lebeau; 21.09.2020