Задача: написать программу, которая будет иметь 3 основных класса для работы, которые реализую в свою очередь весь функционал программы: лист задач, управляющий класс задачи, класс выделения потоков для решения задачи.
Этот проект является реальным и может быть применен вами в решении своих задач. Тут мы не будем поднимать тему интерфейсов, мы рассмотрим нестандартное решение данной задачи, которое уложится в 2 или 3 юнита. Все зависит только от того, как вам удобнее.
Ахтунг! Я не буду полностью расписывать все, я покажу лишь проблемы и их решение без полного листинга. В примерах будут допущены грубейшие ошибки, чтобы наглядно показать проблемы и их решения.
Приведем краткое описание классов:
TContolTask - лист задач
TChecker - выполнение задачи
TThreadChecker - потоки для задачи
Опишем стандартно классы:
Как вас ранее учили: каждый класс в своем юните.
unit ContolTask ;
unit Checker;
unit ThreadChecker;
Ну что ж, давайте быстро опишем классы:
В юнит с чекером мы подключаем юнит с классом потоков и наоборот, чтобы мы могли ссылаться друг на друга, а также наследуем класс (!_это_вынужденная_ошибка), чтобы иметь доступ к Work. Из этого всего мы получим ошибку:
Вложенный класс представляет из себя класс в классе, а именно:
В данном примере показано как правильно вложить класс 2 в класс 1. В данном случае мы еще не будем иметь доступ к переменной Work из вложенного класса, так как данная переменная принадлежит конкретному экземпляру класса, а не классу. Пример ошибки:
Как мы видим, мы не можем обращаться таким образом. Исправить это можно следующем образом: мы определяем данную переменную не как экземпляр класса, а как принадлежащую классу
Полный листинг примера:
Перенесем данный пример на реальную задачу и получим следующую картину:
Вам остается лишь расставить права доступа. Теперь наши потоки могут ссылаться на родительский класс и его переменные для дальнейшего выполнения работы. В классе задач вы создаете экземпляр класса и дописываете управление.
P.S. Хочу сразу сказать для любителей использовать синхронизацию в потоках: в данном примере НЕ ДОЛЖНО быть никакого общения с VCL потоком путем вызова стандартной функции синхронизации. Вы должны использовать класс статистики от родителя и передавать любые значения через критическую секцию. Вывод осуществлять через поток VCL (опрашивать вложенный класс статистики через менеджер задач).
Этот проект является реальным и может быть применен вами в решении своих задач. Тут мы не будем поднимать тему интерфейсов, мы рассмотрим нестандартное решение данной задачи, которое уложится в 2 или 3 юнита. Все зависит только от того, как вам удобнее.
Ахтунг! Я не буду полностью расписывать все, я покажу лишь проблемы и их решение без полного листинга. В примерах будут допущены грубейшие ошибки, чтобы наглядно показать проблемы и их решения.
Приведем краткое описание классов:
TContolTask - лист задач
TChecker - выполнение задачи
TThreadChecker - потоки для задачи
Опишем стандартно классы:
Как вас ранее учили: каждый класс в своем юните.
unit ContolTask ;
unit Checker;
unit ThreadChecker;
Ну что ж, давайте быстро опишем классы:
Код:
type
TContolTask = class (TObject)
public
constructor Create; virtual;
strict private
Workers: array[1..10] of TChecker;
destructor Destroy; virtual;
end;
Код:
type
TChecker = class (TObject)
public
private
Work: Boolean;
ThreadCkecker: TThreadChecker;
constructor Create(link: WideString); virtual;
destructor Destroy; virtual;
protected
end;
Код:
type
TThreadChecker = class (TChecker)
public
private
procedure Execute;
constructor Create(link: WideString); override;
destructor Destroy; override;
protected
end;
F2047 Circular unit reference to
, что и требовалось доказать. Мы попали в круговые ссылки и при разных вариантах компиляции у нас ошибки могут отличаться. Вторая ошибка, которая может возникнуть - это Out of memory
, что также свидетельствует, что компилятор задыхается от того, что у нас 2 юнита ссылаются друг на друга. В данном случае решением будет использование интерфейсов. Это решение было многократно описано в Интернете, и оно нам не интересно. Я предлагаю поступить иначе и решить задачу не таким популярным способом, как вложенные класс.Вложенный класс представляет из себя класс в классе, а именно:
Код:
type Cl1 = class(TObject)
public
Work: Bool;
type Cl2 = class(TObject)
public
procedure A;
private
protected
end;
private
TCL: Cl2;
protected
end;
[dcc32 Error] Unit1.pas(52): E2124 Instance member 'Work' inaccessible here
Как мы видим, мы не можем обращаться таким образом. Исправить это можно следующем образом: мы определяем данную переменную не как экземпляр класса, а как принадлежащую классу
class var Work: bool;
. Теперь мы можем собрать проект.Полный листинг примера:
Код:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
type Cl1 = class(TObject)
public
class var Work: Boolean;
// Work: Boolean;
type Cl2 = class(TObject)
public
procedure A;
private
protected
end;
private
TCL: Cl2;
protected
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ Cl1.Cl2 }
procedure Cl1.Cl2.A;
begin
while Work do
begin
end;
end;
end.
Код:
type
TChecker = class (TObject)
public
class var baseFile: file;
class var ThreadWork: Boolean;
type TThreadChecker = class(TThread)
strict private
function CheckRegExpr(line :string): Boolean;
function GetCheckedLine(line: string): string;
procedure CheckLinsDB(out base: TStringList);
procedure AddLinsDB(out base: TStringList);
procedure Execute;
destructor Destroy; override;
protected
public
published
constructor Create(link: WideString); virtual;
end;
type TStats = class
strict private
protected
_iPrivateList, _iPublicList: Int64;
_GoodLine, _BadLine, _NextLine, _MaxLine, _ProgressLine: Int64;
_CountDataBase: Int64;
public
property CountDataBase: Int64 read _CountDataBase write _CountDataBase;
property GoodLine: Int64 read _GoodLine write _GoodLine;
property BadLine: Int64 read _BadLine write _BadLine;
property NextLine: Int64 read _NextLine write _NextLine;
property MaxLine: Int64 read _MaxLine write _MaxLine;
property ProgressLine: Int64 read _ProgressLine write _ProgressLine;
property iPrivateList: Int64 read _iPrivateList write _iPrivateList;
property iPublicList: Int64 read _iPublicList write _iPublicList;
published
constructor Create;
end;
strict private
baseLink: string;
iBase: Integer;
ThreadChecker: TThreadChecker;
Stats: TStats;
destructor Destroy; virtual;
procedure Execute;
protected
published
constructor Create(link: WideString); virtual;
end;
Вам остается лишь расставить права доступа. Теперь наши потоки могут ссылаться на родительский класс и его переменные для дальнейшего выполнения работы. В классе задач вы создаете экземпляр класса и дописываете управление.
P.S. Хочу сразу сказать для любителей использовать синхронизацию в потоках: в данном примере НЕ ДОЛЖНО быть никакого общения с VCL потоком путем вызова стандартной функции синхронизации. Вы должны использовать класс статистики от родителя и передавать любые значения через критическую секцию. Вывод осуществлять через поток VCL (опрашивать вложенный класс статистики через менеджер задач).
Тема открыта для предложений и критики.