При работе с компонентом TStringGrid в Delphi часто возникает необходимость хранить в ячейках не только строки, но и дополнительные данные. Для этого используется свойство Objects, которое позволяет привязать к ячейке любой объект типа TObject. Однако при смешанном хранении простых целых чисел и сложных объектов возникают проблемы с определением типа данных и их корректным освобождением.
Проблема
Как видно из обсуждения на форуме, основная проблема заключается в том, что:
Некоторые ячейки содержат просто целые числа, приведённые к типу TObject: MySg.Objects[c,r] := TObject(MyNumber);
Другие ячейки содержат сложные объекты с конструкторами и деструкторами.
При попытке освободить память возникает ошибка доступа (AV - Access Violation), если ячейка содержит простое число, а не объект.
Опасные решения
Некоторые предложенные решения могут быть опасны:
Попытка получить ClassName: s := '';
try
s := Objects[c,r].ClassName;
except end;
if s <> '' then
Objects[c,r].Free;
Это решение ненадёжно, так как обращение к невалидному объекту может вызвать непредсказуемые последствия, включая повреждение памяти.
Использование отрицательных чисел: Хранение чисел как отрицательных значений, а объектов - как положительных. Это решение хрупко и может не работать на всех платформах.
Рекомендуемые решения
1. Использование общего базового класса (рекомендуется)
Создайте базовый класс для всех данных, хранимых в StringGrid:
type
TGridData = class
public
destructor Destroy; override;
end;
TGridInteger = class(TGridData)
private
FValue: Integer;
public
constructor Create(AValue: Integer);
property Value: Integer read FValue write FValue;
end;
TGridComplexObject = class(TGridData)
// Реализация вашего сложного объекта
end;
constructor TGridInteger.Create(AValue: Integer);
begin
inherited Create;
FValue := AValue;
end;
destructor TGridData.Destroy;
begin
inherited Destroy;
end;
Использование:
// Запись целого числа
MySg.Objects[c,r] := TGridInteger.Create(MyNumber);
// Запись сложного объекта
MySg.Objects[c,r] := TGridComplexObject.Create(...);
// Проверка типа и освобождение
if Assigned(MySg.Objects[c,r]) then
MySg.Objects[c,r].Free;
Преимущества: - Единообразная обработка всех данных - Безопасное освобождение памяти - Возможность проверки типа через is
2. Использование Tagged Pointers (для опытных)
Если важно сохранить производительность, можно использовать технику "tagged pointers", как предложил Remy Lebeau:
const
MaxObjIntValue = 8;
procedure SetGridValue(SG: TStringGrid; ACol, ARow: Integer; AValue: NativeInt);
begin
if (AValue >= 0) and (AValue <= MaxObjIntValue) then
SG.Objects[ACol, ARow] := TObject((AValue shl 1) or $1)
else
SG.Objects[ACol, ARow] := TObject(AValue);
end;
function GetGridValue(SG: TStringGrid; ACol, ARow: Integer; out IsObject: Boolean): NativeInt;
var
Value: NativeInt;
begin
Value := NativeInt(SG.Objects[ACol, ARow]);
IsObject := (Value and $1) = 0;
if IsObject then
Result := Value
else
Result := Value shr 1;
end;
procedure FreeGridObject(SG: TStringGrid; ACol, ARow: Integer);
begin
if (NativeInt(SG.Objects[ACol, ARow]) and $1) = 0 then
SG.Objects[ACol, ARow].Free;
end;
3. Использование отдельного хранилища данных
Альтернативный подход - создать отдельную структуру данных для хранения дополнительной информации о ячейках и связывать её с StringGrid через индексы или идентификаторы.
Заключение
Наиболее безопасным и поддерживаемым решением является использование общего базового класса для всех типов данных, хранимых в StringGrid. Это обеспечивает:
Типобезопасность
Корректное освобождение памяти
Простоту расширения функциональности
Читаемость кода
Избегайте "хаков" с приведением типов и недокументированными возможностями, так как они могут привести к трудноуловимым ошибкам и проблемам с переносимостью кода.
Статья описывает методы безопасного хранения и освобождения объектов разных типов в компоненте TStringGrid Delphi, предотвращая ошибки доступа к памяти.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.