C++ Virtual Keyword [tr]

Default olarak, C++ compile time’da bir fonksiyon çağrısını bu fonksiyonun doğru tanımı ile karşılaştırır. Bu işleme static binding denir. Programcı bu işlemi run time da yapabilir, bu işleme de dynamic binding adı verilir. Eğer programcı dynamic binding yapmak istiyorsa, fonksiyonun başına virtual yazarak yapabilir.

Aşağıdaki örneklerde static ve dynamic binding’ler gösterilmektedir.

#include “iostream”
using namespace std;

struct A {
void f() { cout << "Class A" << endl; }
};

struct B: A {
void f() { cout << "Class B" << endl; }
};

void g(A& arg) {
arg.f();
}

int main() {
B x;
g(x);
}


bu kod parçasının output’u ‘Class A’ olacaktır. g fonksiyonu çağırıldığı zaman içerisinde object’e B tipine sahip olsa dahi A tipinin f fonksiyonu çağrılır. Compile time’da compiler g fonksiyon argümanının A’dan türeyen bir object’e referans ettiğini bilir. Compiler argümanın A tipinde mi B tipinde mi olduğuna karar veremez. Öte yandan bu olay run time’da yapılabilir. Aşağıdaki kod örneği A::f() fonksiyonunun virtual olması dışında tamamen yukarıdaki kod örneği ile
aynıdır.

#include “iostream”
using namespace std;

struct A {
virtual void f() { cout << "Class A" << endl; }
};

struct B: A {
void f() { cout << "Class B" << endl; }
};

void g(A& arg) {
arg.f();
}

int main() {
B x;
g(x);
}


yukarıdaki kod parçasının çıktısı ‘Class B’ olacaktır. Virtual keyword’u argümanın tipine bakmaksızın f fonksiyonunun tanımını seçer ama object’nin tipi yine B tipine referans eder.

Böylelikle programcı diğer türemiş class’lar için tekrar tanımladığı memberr function olan virtual fonksiyon, compiler tarafından aldığı argümana göre yeniden tanımlanacaktır. Hatta programcı argüman tipinin base class’ına referans eden bir pointer’la dahi fonksiyonu çağırsa da işlem yine aynı olacaktır.

Virtual fonksiyonu declare eden veya inherit eden class ‘polymorphic class’ olarak çağrılır.

Programcı herhangi bir türetilmiş class içerisinde member function olan bir virtual fonksiyon yazabilir. Mesela; A class’ı içerisinde bir virtual fonksiyon tanımladık ve B class’ı A class’ını direct ya da indirect şekilde türetiyor. Eğer B class’ı içerisindeki f fonksiyonu A class’ı içerisindeki f fonksiyonu ile aynı isim ve aynı parametre listesine sahipse yani imzaları aynı ise B class’ının da f fonksiyonu virtual’dır. (B class’ı içerisindeki f fonksiyonu virtual olarak declare edilmesi bile) Öte yandan B class’ı içerisindeki f fonksiyonu A class’ı içerisinde f fonksiyonu ile aynı isimlere sahip olsalar dahi parametre listesi farklı olduğunda B class’ı içerisindeki f fonksiyonu, A class’ı içerisindeki f fonksiyonunu override etmiş olmaz ve bu nedenle virtual olamaz. (B class’ı içerisinde f fonksiyonu virtual olarak declare edilmiş olsa bile)

#include “iostream”
using namespace std;

struct A {
virtual void f() { cout << "Class A" << endl; }
};

struct B: A {
void f(int) { cout << "Class B" << endl; }
};

struct C: B {
void f() { cout << "Class C" << endl; }
};

int main() {
B b; C c;
A* pa1 = &b;
A* pa2 = &c;

pa1->f();
pa2->f();
}


Yukarıdaki kod parçasının çıktısı ;

Class A
Class C


olacaktır. Çünkü B class’ı içerisinde f fonksiyonu virtual değildir ve A class’ı içerisindeki f fonksiyonunu gölgeler. Bu sebeple compiler b.f() çağrısına izin vermeyecektir. C class’ı içerisindeki f fonksiyonu virtual’dır, A içerisindeki f fonksiyonunu C class’ı içerisinde görünmez olsa bile f fonksiyonunu override eder.

Programcı base class içerisindeki destructor virtual olarak tanımlarsa türetilmiş class’ların da destructuor’ı base class’ın destructor’ını override etmiş gibi olacaktır. (Destructor’ların override edilemeyeceği gerçeğine rağmen) Override edilen virtual fonksiyonun return tipi override eden virtual fonksiyonun return tipinden farklı olabilir. Bu durumda override edilen fonksiyona ‘covariant virtual fonksiyon’ adı verilir. B class’ı A class’ının virtual f fonksiyonunu override ediyor fakat return tipleri farklı ise aşağıdaki durumlar oluşabilir.

B class’ının f fonksiyonu T tipindeki bir object’nin reference’nı ya da pointer’ını return eder ve A class’ının f fonksiyonu da açık olarak direct ya da indirect şekilde T class’ının base class’ı olan class’ın reference’ını ya da pointer’ı return eder.B class’ının f fonksiyonu tarafından return edilen const veya volatile pointer ya da reference niteliği, A class’ının f fonksiyonu tarafından return edilen reference ya da pointer’ın niteliğinden daha az veya aynıdır. B class’ının f fonksiyonunun return tipi ya B declaration’ının içinde olmalı (f fonksiyonu implement edilmeli) veya B tipinde olmalıdır.

#include “iostream”
using namespace std;

struct A { };

class B : private A {
friend class D;
friend class F;
};

A global_A;
B global_B;

struct C {
virtual A* f() {
cout << "A* C::f()" << endl;
return &global_A;
}
};

struct D : C {
B* f() {
cout << "B* D::f()" << endl;
return &global_B;
}
};

struct E;

struct F : C {

// Error:
// E is incomplete
// E* f();
};

struct G : C {

// Error:
// A is an inaccessible base class of B
// B* f();
};

int main() {
D d;
C* cp = &d;
D* dp = &d;

A* ap = cp->f();
B* bp = dp->f();
};


Yukarıdaki kod parçasının ekran çıktısı ;

B* D::f()
B* D::f()


bu şekilde olacaktır.

A* ap = cp->f() ifadesi D::f() fonksiyonunu çağırır ve pointer’ı A* tipine dönüştürür. B* bp = dp->f() ifadesi de D::f() fonksiyonunu çağırır ama return tipini B* tipine dönüştürmez. Compiler virtual F::f() fonksiyonunu tanımlamaya izin vermeyecektir çünkü E complete bir sınıf değildir. Compiler G::f() fonksiyonunun tanımına da izin vermez çünkü A sınıfı, B sınıfının erişilebilir bir base class’ı değildir. (D ve F sınıfı arkadaş sınıf olmasına rağmen B sınıfı G sınıfına member’larına erişme izni vermez) Virtual fonksiyonular global veya static olamazlar çünkü tanım olarak virtual fonksiyon bir base class’ın member function’ıdır ve fonksiyon implementasyonunun kim tarafından çağırıldığının belirlenmesi ilkesine dayanır. Programcı, virtual fonksiyonu başka bir class’ın arkadaşı olabilmek için tanımlayabilir.

Eğer fonksiyon base class içerisinde virtual olarak tanımlanmışsa, programcı binary resolution operator(::) ile kullanarak direk olarak fonksiyona erişebilir. Bu durumda virtual fonksiyon çağrı mekanizması engellenir ve base class içerisinde tanımlanmış fonksiyon implementasyonu kullanılır. Ek olarak, programcı türetilmiş class içerisinde virtual member fonksiyonu override etmemişse, fonksiyon base class içerisindeki fonksiyon içeriği direk olarak alarak, bu içeriği kullanır.

Virtual fonksiyolar ;

Implement edilebilir,
Pure olarak declare edilebilir,
Hem implement edilip hem de pure olarak declare edilebilir.
Base class bir ya da daha fazla pure virtual fonksiyon içeriyorsa ‘abstract class’ olarak adlandırılır.

Kaynak 

Popular posts from this blog

Polya’nın Problem Çözme Teknikleri

Mikroislemci Temelleri