Эта строка вызывает вашу ошибку:
return ((lhs.numerator() * rhs.denominator()) + (lhs.denominator() * rhs.numerator()), lhs.denominator() * rhs.denominator());
Проблема в том, что она оценивается как целое, что дает вам целое число. Это целое число затем передается неявно вызванному конструктору вашего класса Rational
. Обратите внимание, что в этой строке используется запятая. Для кода, подобного a, b
, он сначала оценивает a
, отбрасывает результат, а затем оценивает b
и сохраняет этот результат как результат общего выражения. Это не то, что вы хотели.
Вот то, как я сделаю то использование переменной класса, процедуры класса и блока инициализации:
unit MyObject;
interface
type
TMyObject = class
private
class var FLogger : TLogLogger;
public
class procedure SetLogger(value:TLogLogger);
class procedure FreeLogger;
end;
implementation
class procedure TMyObject.SetLogger(value:TLogLogger);
begin
// sanity checks here
FLogger := Value;
end;
class procedure TMyObject.FreeLogger;
begin
if assigned(FLogger) then
FLogger.Free;
end;
initialization
TMyObject.SetLogger(TLogLogger.Create);
finalization
TMyObject.FreeLogger;
end.
Перед версией 7 Delphi не имел статических переменных, необходимо будет использовать глобальную переменную.
Для создания его максимально частным поместите его в implementation
раздел единицы.
TMyObject = class
private
class var FLogger : TLogLogger;
procedure SetLogger(value:TLogLogger);
property Logger : TLogLogger read FLogger write SetLogger;
end;
procedure TMyObject.SetLogger(value:TLogLogger);
begin
// sanity checks here
FLogger := Value;
end;
Примечание, что эта переменная класса будет перезаписываема от любого экземпляра класса, следовательно можно настроить его где-то в другом месте в коде, обычно на основе некоторого условия (тип регистратора и т.д.).
Редактирование: Это также будет то же во всех потомках класса. Измените его в одном из детей, и это изменяется для всех порожденных экземпляров. Вы могли также настроить обработку экземпляра по умолчанию.
TMyObject = class
private
class var FLogger : TLogLogger;
procedure SetLogger(value:TLogLogger);
function GetLogger:TLogLogger;
property Logger : TLogLogger read GetLogger write SetLogger;
end;
function TMyObject.GetLogger:TLogLogger;
begin
if not Assigned(FLogger)
then FLogger := TSomeLogLoggerClass.Create;
Result := FLogger;
end;
procedure TMyObject.SetLogger(value:TLogLogger);
begin
// sanity checks here
FLogger := Value;
end;
В прошлом году Hallvard Vassbotn вел блог о взломе Delphi, который я сделал для этого, это стало статьей с двумя частями:
Да, это - долгое чтение, но очень полезный.
, Таким образом, я снова использовал запись VMT (устаревшую), названную vmtAutoTable как переменная. Этот слот в VMT может использоваться для хранения любого 4-байтового значения, но если Вы хотите сохранить, Вы могли бы всегда выделять запись со всеми полями, которых Вы могли пожелать.
Ключевые слова, которые Вы ищете, являются "var класса" - это запускает блок переменных класса в Вашем объявлении класса. Необходимо закончить блок "var", если Вы хотите включать другие поля после него (иначе, блок может быть закончен "частным", "общедоступным", "процедура" и т.д. спецификатор). Например,
(Редактирование: Я перечитал вопрос и переместил подсчет ссылок в TMyClass - поскольку Вы не можете редактировать класс TMySharedObjectClass, который Вы хотите совместно использовать, если он прибывает из чужой библиотеки)
TMyClass = class(TObject)
strict private
class var
FMySharedObjectRefCount: integer;
FMySharedObject: TMySharedObjectClass;
var
FOtherNonClassField1: integer;
function GetMySharedObject: TMySharedObjectClass;
public
constructor Create;
destructor Destroy; override;
property MySharedObject: TMySharedObjectClass read GetMySharedObject;
end;
{ TMyClass }
constructor TMyClass.Create;
begin
if not Assigned(FMySharedObject) then
FMySharedObject := TMySharedObjectClass.Create;
Inc(FMySharedObjectRefCount);
end;
destructor TMyClass.Destroy;
begin
Dec(FMySharedObjectRefCount);
if (FMySharedObjectRefCount < 1) then
FreeAndNil(FMySharedObject);
inherited;
end;
function TMyClass.GetMySharedObject: TMySharedObjectClass;
begin
Result := FMySharedObject;
end;
, обратите внимание, что вышеупомянутое не ориентировано на многопотоковое исполнение, и могут быть лучшие способы подсчета ссылок (такие как использование Интерфейсов), но это - простой пример, который должен запустить Вас. Обратите внимание, что TMySharedObjectClass может быть заменен TLogLogger или независимо от того, что Вам нравится.
Поскольку, что я хочу сделать (частный постоянный класс), самое опрятное решение, которое я могу предложить (на основе ответов до сих пор):
unit MyObject;
interface
type
TMyObject = class
private
class var FLogger: TLogLogger;
end;
implementation
initialization
TMyObject.FLogger:= TLogLogger.GetLogger(TMyObject);
finalization
// You'd typically want to free the class objects in the finalization block, but
// TLogLoggers are actually managed by Log4D.
end.
, Возможно, немного более объектно-ориентированный было бы что-то как:
unit MyObject;
interface
type
TMyObject = class
strict private
class var FLogger: TLogLogger;
private
class procedure InitClass;
class procedure FreeClass;
end;
implementation
class procedure TMyObject.InitClass;
begin
FLogger:= TLogLogger.GetLogger(TMyObject);
end;
class procedure TMyObject.FreeClass;
begin
// Nothing to do here for a TLogLogger - it's freed by Log4D.
end;
initialization
TMyObject.InitClass;
finalization
TMyObject.FreeClass;
end.
, Который мог бы иметь больше смысла, если бы было несколько таких констант класса.
В Delphi статические переменные реализованы как константы тип переменных :)
Это могло быть несколько вводящим в заблуждение.
procedure TForm1.Button1Click(Sender: TObject) ;
const
clicks : Integer = 1; //not a true constant
begin
Form1.Caption := IntToStr(clicks) ;
clicks := clicks + 1;
end;
И да, другая возможность использует глобальную переменную в implementation
часть Вашего модуля.
Это только работает, если переключатель компилятора "Присваиваемый Consts" включен, глобально или с {$J+}
синтаксис (tnx Lars).
Два вопроса я думаю, что должен быть отвечен перед предложением "идеального" решения..
Код:
interface
type
TMyObject = class(TObject)
private
FLogger: TLogLogger; //NB: pointer to shared threadvar
public
constructor Create;
end;
implementation
threadvar threadGlobalLogger: TLogLogger = nil;
constructor TMyObject.Create;
begin
if not Assigned(threadGlobalLogger) then
threadGlobalLogger := TLogLogger.GetLogger(TMyObject); //NB: No need to reference count or explicitly free, as it's freed by Log4D
FLogger := threadGlobalLogger;
end;
Редактирование: кажется, что переменные класса глобально хранятся, а не экземпляр на поток. См. этот вопрос для деталей.
Ну, это не красиво, но отлично работает в Delphi 7:
TMyObject = class
pulic
class function MySharedObject: TMySharedObject; // I'm lazy so it will be read only
end;
implementation
...
class function MySharedObject: TMySharedObject;
{$J+} const MySharedObjectInstance: TMySharedObject = nil; {$J-} // {$J+} Makes the consts writable
begin
// any conditional initialization ...
if (not Assigned(MySharedObjectInstance)) then
MySharedObjectInstance = TMySharedOject.Create(...);
Result := MySharedObjectInstance;
end;
Я сейчас использую его для создания одиночных объектов.