Method Argüman Yönleri - input, output, inout ve ref Farkı
Gün 2: Method Argüman Yönleri | Yönlerin farkları
Bu derste bir task veya function'a argüman geçerken kullanılan dört yönü (input, output, inout, ref) ve özellikle sınıf handle'ları söz konusu olduğunda aralarındaki davranış farklarını öğreneceğiz. Bu farkları kavramak, doğrulama ortamında nesnelerin beklenmedik şekilde değişmesini (veya değişmemesini) anlamak için kritiktir.
Argüman Yönleri ve Kopyalama Mantığı
Bir handle (sınıf değişkeni) aslında bir nesneye işaret eden adrestir. Bir metoda handle geçtiğinizde, yönün ne olduğu adresin mi yoksa nesnenin mi etkileneceğini belirler. Burada iki ayrı kavramı birbirinden ayırmak gerekir: handle'ın işaret ettiği nesnenin içeriğini değiştirmek (d.value = 10) ile handle'ı başka bir nesneye yönlendirmek (d = new(...)).
input: Dışarıdaki my_obj adresini içeriye kopyalar. İçerideki d.value = 10 dediğinde asıl nesneyi değiştirirsin (çünkü aynı adrese bakıyorlar). Ama içeride d = new(99) dediğinde, sadece içerideki kopya etiketin yönünü değiştirmiş olursun. Task bitince o kopya etiket silinir, dışarıdaki my_obj eski nesneye bakmaya devam eder.output: İçeriye hiçbir bilgi almaz. İçeride sıfırdan bir adres yaratır (new). Task bittiği saniye, bu yeni adresi dışarıdaki my_obj'ye zorla yazar.inout: input ve output'un birleşimidir. Dışarıdaki adresi alır, içeride kullanmana izin verir. Eğer içeride handle'ı başka bir yere yönlendirirsen (new(30)), task bittiği an bu yeni adresi dışarıya kopyalar.ref: Kopya yoktur. İçerideki d ile dışarıdaki my_obj kelimenin tam anlamıyla aynı değişkendir. İçeride handle'a new() atadığın an, dışarıdaki değişken de anında o yeni nesneye bakmaya başlar (task'ın bitmesini bile beklemez). Performans açısından en hızlısıdır çünkü adres kopyalama işlemi bile yapılmaz.
Kaynak Kod
// =============================================================================
// GUN 2 Konu 2.1 - ARGÜMAN YÖNLERİ: input vs output vs inout vs ref
// =============================================================================
class Dummy;
int value;
// Kurucu Metot (Constructor)
function new(int v = 0);
value = v;
endfunction
endclass
module arg_directions_demo;
// 1. INPUT: İçeri giren handle'ı (işaretçiyi) kopyalar.
// Çağrıldığı yerdeki asıl handle'ın işaret ettiği yeri değiştiremez,
// ancak nesnenin İÇERİĞİNİ değiştirebilir.
task automatic test_input(input Dummy d);
// Nesnenin içeriğini değiştirmek dışarıdan görünür
d.value = 10;
// Handle'ın kendisini değiştirmek dışarıdan GÖRÜNMEZ
d = new(99);
endtask
// 2. OUTPUT: Başlangıçta null'dır. Task bittiğinde handle'ı DIŞARI kopyalar.
task automatic test_output(output Dummy d);
// Task bittikten sonra dışarıdaki handle bu yeni nesneyi işaret edecektir
d = new(20);
endtask
// 3. INOUT: Handle'ı İÇERİ kopyalar ve task bittiğinde DIŞARI kopyalar.
task automatic test_inout(inout Dummy d);
// Gelen nesneyi değiştirir
d.value = 35;
// Yeni bir nesneye işaret eder, task bittikten sonra dışarısı bu yeni adresi görecektir
d = new(30);
endtask
// 4. REF: Handle'ın GERÇEK hafıza referansını iletir (kopyalama yapılmaz).
// Handle üzerindeki herhangi bir değişiklik anında dışarıdan görünür.
task automatic test_ref(ref Dummy d);
// Dışarıdaki handle anında bu yeni nesneyi işaret etmeye başlar
d = new(40);
endtask
initial begin
Dummy my_obj;
$display("=== Argüman Yönleri Demosu ===\n");
// --- INPUT ---
my_obj = new(1);
test_input(my_obj);
// my_obj.value 99 DEĞİL, 10 olacaktır. 'new(99)' task bittiğinde silindi.
$display("[INPUT] test_input sonrasi : my_obj.value = %0d", my_obj.value);
// --- OUTPUT ---
// my_obj adresi tamamen üzerine yazılır.
test_output(my_obj);
$display("[OUTPUT] test_output sonrasi: my_obj.value = %0d", my_obj.value);
// --- INOUT ---
test_inout(my_obj);
// my_obj artık test_inout içinde oluşturulan yeni nesneyi işaret ediyor.
$display("[INOUT] test_inout sonrasi : my_obj.value = %0d", my_obj.value);
// --- REF ---
test_ref(my_obj);
// my_obj anında doğrudan değiştirildi.
$display("[REF] test_ref sonrasi : my_obj.value = %0d", my_obj.value);
$finish;
end
endmodule
Kodun Açıklaması
Dummysınıfı: Tek birvalueözelliği ve onu başlatannew(int v = 0)constructor'ı içerir. Yön farklarını gözlemlemek için sade bir taşıyıcı olarak kullanılır.test_input:d.value = 10satırı gelen nesnenin içeriğini değiştirir ve bu dışarıdan görünür. Ancakd = new(99)yalnızca içerideki yerel handle'ı yeni bir nesneye yönlendirir; task bitince bu kopya kaybolur. Bu yüzden çıktıdamy_obj.value99 değil 10'dur.test_output: Argüman task'a girerkennullkabul edilir; içeride hiçbir giriş bilgisi kullanılmaz.d = new(20)ile oluşturulan yeni nesne, task bittiğinde dışarıdakimy_obj'ye kopyalanır. Sonuçmy_obj.value = 20olur.test_inout: Hem girişte hem çıkışta kopyalama yapar.d.value = 35gelen nesneyi değiştirir, ama sonrad = new(30)ile yeni bir nesneye geçilir. Task bittiğinde dışarı kopyalanan bu son handle olduğundanmy_obj.value = 30olur.test_ref:refile kopya yapılmaz; içeridekidile dışarıdakimy_objaynı değişkendir.d = new(40)çağrıldığı andamy_objda yeni nesneyi işaret etmeye başlar; task'ın bitmesini beklemez. Sonuçmy_obj.value = 40olur.automaticanahtar kelimesi: Tüm task'larautomaticolarak tanımlanmıştır; bu, her çağrıda argümanların ve yerel değişkenlerin kendi kopyasının oluşturulmasını sağlar (özyineleme ve yeniden giriş güvenliği için gereklidir).
Önemli Noktalar
inputile gelen bir handle üzerinden nesnenin içeriğini değiştirebilirsiniz, fakat handle'ı yeni bir nesneye yönlendirmeniz dışarıyı etkilemez.outputargümanına metoda girerken güvenmeyin; başlangıçtanullkabul edilir ve yalnızca metot bitiminde dışarı yazılır.inoutveoutput, değeri metot bittiğinde dışarı kopyalar; bu nedenle değişiklik metot sırasında değil, dönüşte görünür.refen hızlısıdır (adres kopyalama bile yapılmaz) ve değişiklikler anında dışarıya yansır; ancak bu güç, istenmeyen yan etkilere de yol açabileceği için dikkatli kullanılmalıdır.- Yalnızca okumak için bir nesneyi geçiyorsanız ve onu değiştirmeyeceğseniz
const reftercih edilebilir; bu hem performans sağlar hem de kazara değişikliği önler.