10 Ekim 2017 Salı

Forward Declaration - Class İçin

Giriş
Derleyici sınıflara eriştiği zaman, sınıfın "complete type" olmasını ister. 

Örnek - metodun bulunamaması
Bu örneği sadece C++'ın C'ye göre çok daha sıkı kurallara sahip olduğunu göstermek için aldım. Yoksa örnekte class değil, metod bulunamıyor. 

C90 ile "imlicit function decleration" özelliği vardı ve aşağıdaki kod derlenirdi. Ancak bu özellik C99 ile kaldırıldı. C++ ise C99'dan daha da sıkı kurallara sahip.

Aşağıdaki örnekte sum() metodu scope içinde olmadığı için derleme hatası alırız.
#include<stdio.h>

int main()
{
  int a=sum(4,6);
  printf("%d",a);
  return 0;
}

int sum(int a,int b)
{
  return a+b;
}
Hata şöyledir.
sum was not declared in this scope
Örnek - sınıfın bulunamaması
Bu bir C++ örneği. Aşağıdaki örnekte student "complete type" olmadığı için derleme hatası alırız.
#include<iostream.h>
#include<conio.h>

void main()
{
  student s;
  s.show();

  getch();
}

class student
{
  int age;
  public:
  void show();
};

void student::show()
{
  age = 5;
  cout << age;
}
Forward Declaration
Yukarıdaki örnekler aslında çok büyük dert değil, çünkü kodda eksik bir şey yok, sadece kodun sırası biraz karışık. Bu durumda derleyiciye yardımcı olmak için "Forward Declaration" yapmak gerekiyor. Yani derleyiciye, sen bu kod parçasını şimdi görmedin ancak ileride göreceksin çalışmaya devam et diyoruz.

Bu durumda derleyici sınıfın tipi, büyüklüğü, üyeleri hakkında hiçbir şey bilmez. Yani sınıf bir "incomplete type" olarak kabul edilir. Ve Incomplete Type ile yine de çalışmak mümkün.

Forward Declaration vs Elaborated Type Specifier
Forward Declaration aslında  Elaborated Type Specifier kullanımının özel bir halinin ismidir. Sadece class ile ilgilenen 3. maddesine değinir. 

Ne Yapılabilir
Incomplete Type'ı X olarak kabul edersek ve elimizde şöyle bir forward declaration varsa. Burada baştaki class kelimesine dikkat!
class X;
1. Member Tanımlanabilir
Şöyle yaparız.
class Foo {
  X *pt;
  X &pt;
};
2. Metod Declare Edilebilir
Şöyle yaparız.
void f1(X);
X    f2();
3. Pointer veya Reference Kullanan Metod Tanımlanabilir - Declare Değil, Define
Şöyle yaparız.
void f3(X*, X&) {}
X&   f4()       {}
X*   f5()       {}
Bir başka örnek
class MyClass;

class MyClass2: public MyBaseClass {
  MyClass2 (MyClass& so);
};

Ne Yapılamaz
Incomplete Type'ı X olarak kabul edersek.

1. Kalıtım Yapılamaz
Şunu yapamayız.
class Foo : X {} // compiler error!
2. Üye Tanımlanamaz
Şunu yapamayız.
class Foo {
  X m; // compiler error!
};
Üye dizisi de tanımlanamaz.
class Foo{
  X myX[10];
};
3. Metod Tanımlanamaz - Declare edilme maddesinden farklı !
Şunu yapamayız.
void f1(X x) {} // compiler error!
X    f2()    {} // compiler error!
Aradaki fark şöyle görülebilir.
struct X;              // Forward declaration of X

void f1(X* px) {}      // Legal: can always use a pointer/reference
X f2(int);             // Legal: return value in function prototype
void f3(X);            // Legal: parameter in function prototype
void f4(X) {}          // ILLEGAL: *definitions* require complete types
4. X'in Alan veya Metodlarını Kullanamayız
Şunu yapamayız.
class Foo {
    X *m;            
    void method()            
    {
        m->someMethod();      // compiler error!
        int i = m->someField; // compiler error!
    }
};
Namespace
Aynı isim alanı içindeki iki sınıf forward declaration satırını da isim alanı içine koymalıdır. A sınıfı B sınıfını şöyle kullanır.
// A.h
namespace ns {
  class B; // forward declare B

  class A {
    public:
        // Some functions with B references as arguments        
  };
}
Cyclic Dependency
Şöyle yaparız.
// A.h (Header guarded)
namespace ns {
  class B; // forward declare B
  class A {
  public:
    float a, b;
    void aFunc(B &b);       
  };
}

// B.h (Header guarded)
namespace ns {
  class A; // forward declare A
  class B {
  public:
    void bFunc(A &a);
    int getRand();
  };
}

// A.cpp
#include "A.h"
#include "B.h"
namespace ns {
  void A::aFunc(B& b) { /* ... */ }
}

// B.cpp
#include "A.h"
#include "B.h"
namespace ns {
  void B::bFunc(A& b) { /* ... */ }
  int B::getRand() { /* ... */ }
}




Hiç yorum yok:

Yorum Gönder