2016-07-05 6 views
-1

Предположим, у меня есть функция:Как передать объекты между функциями?

function someFunction: TStringList; 
begin 
    result:=TStringList.Create; 
    if someConditionIsTrue then 
    result:=doSomething; 
    //other code 
end; 

и функция DoSomething:

function doSomething: TStringList; 
begin 
    result:=TStringList.Create; 
    result.Add(something); 
end; 

Если я запускаю этот код все работает, как хотелось бы, но я все еще интересно, если это «правильный» способ обхода объекта, например, строкового списка?

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

+1

Вы уже вызываете утечку памяти в первом примере кода, даже не используя его нигде. Сначала вы создаете экземпляр, затем полностью игнорируете этот экземпляр и создаете еще один на своем месте. –

+0

Ваш код создает 2 экземпляра TStringlist, что приведет к утечке памяти. И тот, который возвращается некоторой функцией, будет отличаться в зависимости от значения 'someConditionIsTrue'. Ничего, вам не нужно. Создавать в doSomething. – MartynA

+0

Нет «правильного» способа. Вам нужно разработать свою конвенцию о том, какая партия освобождает память ** и строго следовать ей. Кроме того, вам нужно следить за подсказками компилятора, это указывает на проблему с первым фрагментом. –

ответ

6

«Правильный» подход заключается в том, чтобы вы установили свои собственные правила для того, как вещи будут уничтожены. Хорошо создавать объекты в результате функции, но только если вы выполняете свои собственные строгие правила.

В вашем случае SomeFunction имеет утечку памяти. Сначала вы создаете TStringList, а затем, если какое-либо условие выполнено, вы создаете на своем месте еще один TStringList, полностью игнорируя первый. Таким образом, утечка памяти.

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

procedure DoSomething(AList: TStringList); 
begin 
    AList.Add(Something); 
end; 

После того, как вы сделаете это, то SomeFunction должен выглядеть следующим образом:

function someFunction: TStringList; 
begin 
    Result:= TStringList.Create; 
    if someConditionIsTrue then 
    DoSomething(Result); 
    //other code 
end; 

"В stringlists никогда не освобожденный"

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


На этой ноте, единственный раз, когда я когда-либо создать объект в результате функции, когда я герметизирующего несколько строк кода, которые иначе были бы дублируется много раз. Например, создание запроса.

Вместо того чтобы повторять этот код ...

Q:= TADOQuery.Create(nil); 
Q.Connection:= MyDatabaseConnection; 
Q.SetSomeOtherProperties; 

... Я положил его в функцию ...

function CreateQuery: TADOQuery; 
begin 
    Result:= TADOQuery.Create(nil); 
    Result.Connection:= MyDatabaseConnection; 
    Result.SetSomeOtherProperties; 
end; 

Тогда, я могу просто вызвать эту функцию всякий раз, когда мне нужно повторить этот код ...

Q:= CreateQuery; 
+1

Я помню, что вы снабдили меня хорошим ответом на вопрос, который я задал несколько лет назад. Итак, спасибо за ответ еще раз: -] –

+0

CreateQuery утечки исключений возникают после создания объекта –

+0

@David Действительно, просто очень грубый и минимальный образец кода. –

6

В stringlists никогда освобожден

Это проблема сама по себе. Как было отмечено в комментариях, это создает утечки памяти. В общем, я нахмурился над функциями, которые создают объекты и дают право собственности через их результаты.Когда мне это нужно, я обычно называю свою функцию "Create*", чтобы сделать ее максимально явной, чтобы вызывающая сторона отвечала за освобождение памяти.

С этим сказанным, более элегантный шаблон для достижения того, что вам нужно:

procedure someFunction; 
var vStrings : TStringList; 
begin 
    vStrings := TStringList.Create; 
    try 
    if someConditionIsTrue then 
     doSomething(vStrings); 
    //other code 
    finally 
    vStrings.Free; 
    end; 
end; 

procedure doSomething(AStrings : TStringList); 
begin 
    AStrings.Add(something); 
end; 

Если вам действительно нужен ваш «SomeFunction», чтобы вернуть TStringList и не хотите, чтобы получить один с помощью параметра, здесь, как правильно управлять им, чтобы избежать утечек памяти.

function CreateAndInitStrings : TStringList; 
begin 
    Result := TStringList.Create; 
    try 
    if someConditionIsTrue then 
     doSomething(Result); 
    //other code 
    except 
    Result.Free; 
    raise; 
    end; 
end; 
+0

Процедура Err, imo ' SomeFunction' является неудачным; возможно, 'процедура NotActuallyaFunction'. – MartynA

+0

Я сохранил ту же семантику, что и в вопросе, чтобы сделать переписку максимально ясной. –