C ++ 'da ileri bildirimler nelerdir?


215

Şurada: http://www.learncpp.com/cpp-tutorial/19-header-files/

Aşağıdakilerden bahsedilmektedir:

add.cpp:

int add(int x, int y)
{
    return x + y;
}

main.cpp:

#include <iostream>

int add(int x, int y); // forward declaration using function prototype

int main()
{
    using namespace std;
    cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
    return 0;
}

Derleyicinin addderlerken " " ne olduğunu bilmesi için ileri bir bildirim kullandık main.cpp. Daha önce de belirtildiği gibi, kullanmak istediğiniz her işlev için başka bir dosyada yaşayan bildirimler yazmak hızlı bir şekilde yorucu olabilir.

" İleri deklarasyonu " daha ayrıntılı açıklayabilir misiniz ? İşlevde kullanırsak sorun nedir main()?


1
"İleriye dönük bir bildiri" gerçekten sadece bir bildiridir. Bu cevaba bakınız (sonu): stackoverflow.com/questions/1410563/…
sbi

Yanıtlar:


381

Neden C ++ 'da ileri bildirim gerekli?

Derleyici, yazım hataları yapmadığınızdan veya işleve yanlış sayıda argüman iletmediğinizden emin olmak istiyor. Bu nedenle, kullanılmadan önce bir 'add' (veya başka türler, sınıflar veya işlevler) bildirimi gördüğünde ısrar eder.

Bu gerçekten derleyicinin kodu doğrulamak için daha iyi bir iş yapmasına izin verir ve düzgün görünümlü bir nesne dosyası oluşturabilmesi için gevşek uçları toplamasına izin verir. Bir şeyleri iletmek zorunda kalmazsanız, derleyici 'add' fonksiyonunun ne olabileceğine dair olası tüm tahminler hakkında bilgi içermesi gereken bir nesne dosyası üretir. Bağlayıcının, aslında 'hangi' ekleme 'çağırmayı denemek ve çözmek için çok zekice bir mantık içermesi gerekir,' add 'işlevi farklı bir nesne dosyasında yaşıyorsa, linker üretmek için add kullanan biriyle birleşiyorsa bir dll veya exe. Bağlayıcının yanlış eklentiyi alması mümkündür. İnt add (int a, float b) kullanmak istediğinizi ancak yanlışlıkla yazmayı unuttuğunuzu, ancak bağlayıcı zaten var olan bir int add (int a, int b) ve bunun doğru olduğunu düşündüm ve bunun yerine bunu kullandılar. Kodunuz derlenir, ancak beklediğiniz gibi olmaz.

Yani, sadece şeyleri açık tutmak ve tahmin vb. Önlemek için, derleyici kullanılmadan önce her şeyi beyan ısrar ediyor.

Beyan ve tanım arasındaki fark

Bir yana, bir açıklama ile tanım arasındaki farkı bilmek önemlidir. Bir bildirim, bir şeyin neye benzediğini göstermek için yeterli kodu verir, bu nedenle bir işlev için bu, dönüş türü, çağrı kuralı, yöntem adı, bağımsız değişkenler ve türleri içindir. Ancak yöntemin kodu gerekli değildir. Bir tanım için, fonksiyonun koduna ve sonra koduna da ihtiyacınız vardır.

İleri bildirimler oluşturma sürelerini nasıl önemli ölçüde azaltabilir?

Bir işlevin bildirimini, işlevin bir bildirimini içeren üstbilgiyi # dahil ederek geçerli .cpp veya .h dosyanıza alabilirsiniz. Ancak, bu, derlemenizi yavaşlatabilir, özellikle de # başlığınızı programınızın .cpp yerine .hpp içine eklerseniz, # yazdığınız .h'yi içeren her şey tüm başlıkları # içerir Sen de #includes yazdın. Aniden, derleyicide yalnızca bir veya iki işlev kullanmak isteseniz bile derlenmesi gereken sayfalar ve kod sayfaları bulunur. Bundan kaçınmak için, bir ileri bildirimi kullanabilir ve sadece dosyanın üst kısmına işlevin bildirimini kendiniz yazabilirsiniz. Yalnızca birkaç işlev kullanıyorsanız, bu, derlemelerinizi başlık dahil her zaman # içeriğe göre gerçekten daha hızlı hale getirebilir. Gerçekten büyük projeler için,

İki tanımın birbirini kullandığı döngüsel referansları kır

Ayrıca, ileri bildirimler döngüleri kırmanıza yardımcı olabilir. İki fonksiyonun her ikisi de birbirini kullanmaya çalışmaktadır. Bu olduğunda (ve bunu yapmak gerçekten geçerli bir şeydir), bir başlık dosyasını dahil edebilirsiniz, ancak bu başlık dosyası # şu anda yazmakta olduğunuz başlık dosyasını dahil etmeye çalışır .... bu daha sonra # diğer başlığı içerir , bu # yazdığınızı içerir. Her başlık dosyası diğerini # eklemeye çalışırken bir tavuk ve yumurta durumunda sıkışıp kaldınız. Bunu çözmek için, ihtiyaç duyduğunuz parçaları dosyalardan birinde iletebilir ve #include'u bu dosyanın dışında bırakabilirsiniz.

Örneğin:

Car.h Dosyası

#include "Wheel.h"  // Include Wheel's definition so it can be used in Car.
#include <vector>

class Car
{
    std::vector<Wheel> wheels;
};

Dosya Tekerleği.h

Hmm ... Tekerlek bir Arabaya bir işaretçi olduğu için burada Araba beyanı gerekiyor, ancak Car.h buraya derlenemez çünkü derleyici hatasına neden olur. Car.h dahil edilmişse, Wheel.h'yi içeren Car.h'yi içeren ve bu sonsuza kadar devam edecek olan Wheel.h'yi içermeye çalışır, böylece derleyici bir hata oluşturur. Çözüm, bunun yerine Araba ilan etmektir:

class Car;     // forward declaration

class Wheel
{
    Car* car;
};

Eğer Wheel sınıfının araba yöntemlerini çağırması gereken yöntemler varsa, bu yöntemler Wheel.cpp'de tanımlanabilir ve Wheel.cpp artık bir döngüye neden olmadan Car.h'yi ekleyebilir.


4
bir işlev iki veya daha fazla sınıfa uygun olduğunda ileri bildirim de gereklidir
Barun

1
Hey Scott, derleme sürelerinizde: Bildirimi her zaman iletmek ve .cpp dosyasına gerektiği gibi üstbilgileri dahil etmek yaygın / en iyi uygulama olduğunu söyleyebilir misiniz? Cevabınızı okuduktan sonra öyle olmalı, ama herhangi bir uyarı olup olmadığını merak ediyorum?
Zepee

5
@Zepee Bu bir denge. Hızlı kurulumlar için iyi bir uygulama olduğunu söyleyebilirim ve denemenizi tavsiye ederim. Bununla birlikte, tür adları vb. Hala değiştiriliyorsa (araçlar otomatik olarak yeniden adlandırma konusunda iyileşiyor olsa da) biraz çaba gerektirebilir ve korunması ve güncellenmesi gerekebilecek ekstra kod satırları gerekebilir. Yani bir ödünleşim var. Kimsenin rahatsız ettiği kod tabanlarını gördüm. Kendinizi aynı ileri tanımları tekrar ederken bulursanız, bunları her zaman ayrı bir başlık dosyasına koyabilir ve buna dahil edebilirsiniz: stackoverflow.com/questions/4300696/what-is-the-iosfwd-header
Scott Langham

Başlık dosyaları birbirine atıfta bulunduğunda ileri bildirimler gerekir: ie stackoverflow.com/questions/396084/…
Nicholas Hamilton

1
Bunu, takımımdaki diğer geliştiricilerin kod tabanının gerçekten kötü vatandaşları olmasına izin verdim. Ileri beyanı ile bir yorum gerekmiyorsa, // From Car.ho zaman garantili, yolda bir tanım bulmaya çalışırken bazı kıllı durumlar oluşturabilirsiniz.
Dagrooms

25

Derleyici, geçerli çeviri biriminde kullanılan her sembolün geçerli birimde önceden bildirilmiş olup olmadığını arar. Sadece bir kaynak dosyası başlangıcında tüm yöntem imzalarını sağlayan bir stil meselesidir, daha sonra tanımlar verilir. Bunun önemli bir kullanımı, bir sınıfa başka bir sınıfın üye değişkeni olarak bir işaretçi kullanmanızdır.

//foo.h
class bar;    // This is useful
class foo
{
    bar* obj; // Pointer or even a reference.
};

// foo.cpp
#include "bar.h"
#include "foo.h"

Bu nedenle, mümkün olduğunda sınıflarda ileri bildirimler kullanın. Programınızda işlevler varsa (ho başlık dosyalarıyla), başlangıçta prototipler sunmak sadece bir stil meselesidir. Bu durum, üstbilgi dosyasının yalnızca işlevleri olan üstbilgiyle birlikte normal bir programda mevcut olması durumunda olur.


12

C ++ yukarıdan aşağıya ayrıştırıldığı için, derleyici kullanılmadan önce bazı şeyleri bilmelidir. Yani, referans verdiğinizde:

int add( int x, int y )

ana işlevde derleyicinin var olduğunu bilmesi gerekir. Bunu kanıtlamak için ana işlevin altına taşımayı deneyin ve bir derleyici hatası alırsınız.

Yani ' İleri Bildirim ' teneke üzerinde söyledikleridir. Kullanımından önce bir şey ilan ediyor.

Genel olarak, bir başlık dosyasına ileri bildirimler ekler ve ardından bu başlık dosyasını iostream öğesinin içerdiği şekilde dahil edersiniz .


12

C ++ 'da " ileri bildirim " terimi çoğunlukla sadece sınıf bildirimleri için kullanılır . Bir sınıfın "ileri bildirimi" nin neden sadece süslü bir adla basit bir sınıf bildirimi olduğu için bu cevaba bakınız .

Başka bir deyişle, "ileri" terimi terime balast ekler, çünkü herhangi bir bildirim kullanılmadan önce bazı tanımlayıcıları bildirdiği kadar ileriye dönük olarak görülebilir .

( Bir tanımın aksine bir deklarasyonun ne olduğu konusunda , bkz. Bir tanım ve bir deklarasyon arasındaki fark nedir? )


2

Derleyici gördüğü add(3, 4)zaman bunun ne anlama geldiğini bilmesi gerekir. İleri bildirimi ile derleyiciye addiki int alan ve int döndüren bir fonksiyon olduğunu söylersiniz . Bu, derleyici için 4 ve 5'i yığına doğru gösterime koyması ve ekleme ile döndürülen şeyin ne tür olduğunu bilmesi gerektiğinden önemli bir bilgidir.

O zaman, derleyici gerçek uygulaması add, yani nerede olduğu (ya da hatta varsa) ve derlemesi konusunda endişelenmez . Yani, daha sonra görünümü girer sonra bağlayıcı çağrıldığında kaynak dosyaları derleme.


1
int add(int x, int y); // forward declaration using function prototype

"İleriye dönük beyanı" daha ayrıntılı açıklayabilir misiniz? Main () işlevinde kullanırsak sorun nedir?

İle aynı #include"add.h". Biliyorsanız, önişlemci #include, #includeyönergeyi yazdığınız .cpp dosyasında bahsettiğiniz dosyayı genişletir . Bu, eğer yazarsanız #include"add.h", aynı şeyi alırsanız, sanki "ileri deklarasyon" yapıyormuşsunuz demektir.

add.hBu çizginin olduğunu varsayıyorum :

int add(int x, int y); 

1

ile ilgili hızlı bir ek: genellikle bu ileri referansları, işlevin / değişkenin vb. uygulandığı .c (pp) dosyasına ait bir başlık dosyasına koyarsınız. örneğinizde şöyle görünecektir: add.h:

extern int add (int a, int b);

extern anahtar sözcüğü, işlevin gerçekte harici bir dosyada bildirildiğini belirtir (kitaplık vb. de olabilir). main.c'niz şöyle görünecektir:

#Dahil etmek 
#include "add.h"

int main ()
{
.
.
.


Ancak, açıklamaları yalnızca başlık dosyasına koymaz mıyız? Bence bu yüzden fonksiyon "add.cpp" de tanımlanmış ve bu nedenle ileri bildirimler kullanılarak? Teşekkürler.
Sadelik

0

Bir sorun, derleyicinin bilmediği, fonksiyonunuz tarafından ne tür bir değer verildiğini bilmesidir; işlevinin intbu durumda bir döndürdüğü varsayılır, ancak bu yanlış olabileceği kadar doğru olabilir. Başka bir sorun, derleyici bilmiyor, işlevinizin ne tür argümanları beklediğini bilmiyor ve yanlış türden değerler geçiriyorsanız sizi uyaramıyor. Geçerken uygulanan özel "tanıtım" kuralları vardır, kayan nokta değerlerini bildirilmemiş bir işleve (derleyici onları çift yazmak için genişletmek zorundadır) söyler, bu genellikle işlevin beklediği gibi, hataların bulunmasını zorlaştırır işlem esnasında.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.