.İf dosyasında neden #ifndef CLASS_H ve #define CLASS_H kullanıyorsunuz ama .cpp dosyasında neden kullanmıyorsunuz?


137

Her zaman insanların yazdığını gördüm

class.h

#ifndef CLASS_H
#define CLASS_H

//blah blah blah

#endif

Soru şu, neden sınıf işlevleri için tanımları içeren .cpp dosyası için bunu yapmıyorlar?

Diyelim ki bende var main.cppve main.cppiçeriyor class.h. class.hDosya vermez includeşey, bu yüzden nasıl yok main.cppolduğunu bilmek class.cpp?


5
"import", muhtemelen burada kullanmak istediğiniz kelime değildir. Dahil etmek.
Kate Gregory

5
C ++ 'da, dosyalar ve sınıflar arasında bire bir ilişki yoktur. İstediğiniz kadar çok sayıda sınıfı bir dosyaya koyabilirsiniz (hatta nadiren yararlı olsa da bir sınıfı birkaç dosyaya bölebilirsiniz). Bu nedenle makro olmamalı FILE_H, olmamalıdır CLASS_H.
sbi

Yanıtlar:


304

İlk olarak, ilk sorgunuzu ele almak için:

Bunu .h dosyasında gördüğünüzde :

#ifndef FILE_H
#define FILE_H

/* ... Declarations etc here ... */

#endif

Bu, bir başlık dosyasının birden çok kez dahil edilmesini önleyen, çeşitli nedenlerle sorun yaratabilecek bir önişlemci tekniğidir. Projenizin derlenmesi sırasında, her .cpp dosyası (genellikle) derlenir. Basit bir deyişle, bu derleyicinin .cpp dosyanızı alacağı , herhangi bir dosyayı açacağı #included, hepsini tek bir büyük metin dosyasına birleştireceği ve sözdizimi analizi gerçekleştireceği ve nihayetinde bazı ara koda dönüştüreceği, diğerlerini optimize edeceği / gerçekleştireceği anlamına gelir ve son olarak hedef mimari için derleme çıktısı oluşturun. Bu nedenle, bir dosya #includedbir .cpp altında birden çok isedosyasında, derleyici dosya içeriğini iki kez ekleyecektir, bu nedenle bu dosyada tanımlar varsa, bir değişkeni yeniden tanımladığınızı bildiren bir derleyici hatası alırsınız. Dosya derleme işlemindeki önişlemci adımı tarafından işlendiğinde, içeriğine ilk erişildiğinde ilk iki satır FILE_Hönişlemci için tanımlanmış olup olmadığını kontrol eder . Değilse FILE_H, kod ile #endifyönerge arasındaki kodu tanımlayacak ve işlemeye devam edecektir . Dosyanın içeriği önişlemci tarafından bir dahaki sefer görüldüğünde, kontrol FILE_Hişlemi yanlış olur, bu yüzden hemen aşağıya doğru taranır #endifve devam eder. Bu yeniden tanımlama hatalarını önler.

İkinci endişenizi gidermek için:

Genel bir uygulama olarak C ++ programlamasında geliştirmeyi iki dosya türüne ayırıyoruz. Biri .h uzantılıdır ve buna "başlık dosyası" diyoruz. Genellikle işlevler, sınıflar, yapılar, global değişkenler, typedefs, önişleme makroları ve tanımları vb. Bildirirler. Temel olarak, size sadece kodunuz hakkında bilgi sağlarlar. Sonra "kod dosyası" dediğimiz .cpp uzantısına sahibiz. Bu, bu işlevler, sınıf üyeleri, tanımlara ihtiyaç duyan tüm yapı üyeleri, genel değişkenler vb. İçin tanımlar sağlar. Böylece .h dosyası kodu bildirir ve .cpp dosyası bu bildirimi uygular. Bu nedenle, genellikle derleme sırasında her bir .cpp(neredeyse tek görmek asla çünkü bir nesnenin dosya ve daha sonra bu nesneleri bağlamak .Cpp dosya başka dahil .cpp dosyası).

Bu dışsalların nasıl çözüldüğü bağlayıcı için bir iştir. Sizin derleyici işlediğinde main.cpp , bu kod için bildirimleri alır class.cpp dahil ederek class.h . Sadece bu işlevlerin veya değişkenlerin neye benzediğini bilmek gerekir (bu bir bildirimin size verdiği şeydir). Bu yüzden main.cpp dosyanızı bir nesne dosyasına derler ( main.obj olarak adlandırın ). Benzer şekilde, class.cpp bir derlendi class.objdosya. Son yürütülebilir dosyayı üretmek için, bu iki nesne dosyasını birbirine bağlamak için bir bağlayıcı çağrılır. Çözülmemiş harici değişkenler veya işlevler için derleyici, erişimin gerçekleştiği yere bir saplama yerleştirir. Bağlayıcı daha sonra bu saplamayı alır ve listelenen başka bir nesne dosyasındaki kodu veya değişkeni arar ve bulunursa, iki nesne dosyasındaki kodu bir çıktı dosyasına birleştirir ve saplamayı işlevin son konumu ile değiştirir veya değişken. Bu şekilde, main.cpp içindeki kodunuz işlevleri çağırabilir ve class.cpp dosyasındaki değişkenleri kullanabilir VE SADECE class.h .

Umarım bu yardımcı olmuştur.


Son birkaç gündür .h ve .cpp'yi anlamaya çalışıyordum. Bu cevap C ++ öğrenmek için zaman ve ilgi kurtardı. İyi yazılmış. Teşekkürler Justin!
Rajkumar R

Gerçekten harika açıkladın! Belki de resimlerle birlikte cevap oldukça iyi olurdu
alamin

13

CLASS_HBir olan koruma içerir ; aynı başlık dosyasının aynı CPP dosyasına (veya daha doğru bir şekilde aynı çeviri birimine ) birden çok kez dahil edilmesini önlemek için kullanılır ve bu da çoklu tanım hatalarına yol açar.

Tanım gereği, CPP dosyasının içeriği yalnızca bir kez okunduğundan CPP dosyalarında korumalara gerek yoktur.

Sen aynı fonksiyona sahip olduğu korumaları dahil yorumlanır gibi görünüyor import(Java gibi) diğer dillerdeki ifadeleri; Ancak durum böyle değil. #includeKendisi eşdeğerdir importdiğer dillerde.


2
msgstr "aynı CPP dosyasında" "aynı çeviri biriminde" yazmalıdır.
dreamlax

@dreamlax: İyi bir nokta - başlangıçta yazacağım şeydi, ama sonra korumaları içerdiğini anlamayan birinin sadece "çeviri birimi" terimi ile karıştırılacağını düşündüm. Cevabı parantez içine "çeviri birimi" eklemek için düzenleyeceğim - bu her iki dünyanın da en iyisi olmalı.
Martin B

6

En azından derleme aşamasında değil.

Bir c ++ programının kaynak koddan makine koduna çevirisi üç aşamada gerçekleştirilir:

  1. Önişleme - Önişlemci, # ile başlayan satırlar için tüm kaynak kodunu ayrıştırır ve yönergeleri yürütür. Sizin durumunuzda, dosyanızın içeriği class.hsatır yerine eklenir #include "class.h. Üstbilgi dosyanıza birkaç yerde dahil olabileceğiniz için #ifndef, önişlemci yönergesi yalnızca üstbilgi dosyası ilk eklendiğinde tanımlanmadığından , yan tümceler yinelenen bildirim hatalarından kaçınır.
  2. Derleme - Derleyici şimdi tüm önceden işlenmiş kaynak kodu dosyalarını ikili nesne dosyalarına çevirmektedir.
  3. Bağlama - Bağlayıcı, nesne dosyalarını birbirine bağlar (dolayısıyla adı). Sınıfınıza veya yöntemlerinden birine yapılan bir başvuru (class.h dosyasında bildirilmesi ve class.cpp dosyasında tanımlanması gerekir) nesne dosyalarından birinde ilgili ofsete çözümlenir. Sınıfınızın class.cpp adlı bir dosyada tanımlanması gerekmediğinden 'nesne dosyalarınızdan birini' yazıyorum, projenize bağlı bir kütüphanede olabilir.

Özet olarak, bildirimler bir başlık dosyası aracılığıyla paylaşılabilirken, bildirimlerin tanımlarla eşlenmesi bağlayıcı tarafından yapılır.


4

Beyan ve tanım arasındaki fark budur. Başlık dosyaları genellikle yalnızca bildirimi içerir ve kaynak dosya tanımı içerir.

Bir şeyi kullanabilmek için sadece tanımını değil bildirimini bilmeniz gerekir. Sadece bağlayıcının tanımı bilmesi gerekir.

Bu nedenle, bir veya daha fazla kaynak dosyasının içine bir başlık dosyası ekleyeceksiniz, ancak başka bir dosyanın içine bir kaynak dosyası eklemeyeceksiniz.

Ayrıca demek istiyorsun #includeve ithal etmiyorsun.


3

Bu, üstbilgi dosyaları için yapılır, böylece içerik, önceden işlenmiş her kaynak dosyada, birden çok kez dahil edilmiş olsa bile (genellikle diğer üstbilgi dosyalarından içerdiği için) yalnızca bir kez görünür. İlk kez dahil edildiğinde, sembol CLASS_H( dahil etme koruması olarak bilinir ) henüz tanımlanmamıştır, bu nedenle dosyanın tüm içeriği dahil edilir. Bunu yapmak sembolü tanımlar, bu yüzden tekrar eklenirse, dosyanın içeriği ( #ifndef/ #endifbloğunun içinde) atlanır.

Kaynak dosya için bunu yapmaya gerek yoktur, çünkü (normalde) başka dosyalar tarafından dahil edilmez.

Son sorunuz için, class.hsınıfın tanımını ve tüm üyelerinin, ilişkili işlevlerin ve başka herhangi bir şeyin bildirimlerini içermesi gerekir, böylece onu içeren herhangi bir dosya sınıfı kullanmak için yeterli bilgiye sahip olur. Fonksiyonların uygulamaları ayrı bir kaynak dosyasında olabilir; sadece onları aramak için beyanlara ihtiyacınız vardır.


2

main.cpp , class.cpp dosyasında ne olduğunu bilmek zorunda değildir . Sadece kullanacağı işlevlerin / sınıfların açıklamalarını bilmek zorundadır ve bu açıklamalar sınıftadır . H.

Linker, class.h dosyasında bildirilen işlevlerin / sınıfların kullanıldığı yerler ile bunların class.cpp dosyasındaki uygulamaları arasında bağlantı kurar.


1

.cppdosyalar #includediğer dosyalara dahil edilmez (kullanılarak ). Bu nedenle korumaya ihtiyaç duymazlar. yalnızca tüm bunları belirttiğiniz Main.cppiçin uyguladığınız sınıfın adlarını ve imzalarını bilecektir - bu bir başlık dosyasının amacıdır. ( Uyguladığınız kodu doğru bir şekilde açıkladığınızdan emin olmak size bağlıdır .) İçerideki yürütülebilir kod , bağlayıcının çabaları sayesinde yürütülebilir kodun kullanımına sunulacaktır .class.cppclass.hclass.hclass.cppclass.cppmain.cpp


1

.cppGereksiz tekrarlayan mantık derlemesini önlemek için genellikle dosyalar gibi kod modüllerinin bir kez derlenmesi ve birden fazla projede bağlanması beklenir . Örneğin, g++ -o class.cppdaha class.osonra birden fazla projeden kullanıma bağlayabileceğiniz bir ürün üretebilirsiniz g++ main.cpp class.o.

#includeBağdaştırıcı olarak kullanabiliriz , ima ettiğiniz gibi, ancak derleyicimizi daha fazla tuş vuruşuyla ve daha savurgan kodumuz yerine daha az tuş vuruşuyla ve daha az boşa harcanan derleme tekrarıyla nasıl düzgün bir şekilde bağlayacağımızı bildiğimizde saçma olur. derlemenin tekrarı ...

Üstbilgi dosyalarının yine de birden çok projenin her birine dahil edilmesi gerekmektedir, çünkü bu her modül için arabirim sağlar. Bu başlıklar olmasaydı derleyici .odosyalar tarafından sunulan sembollerin hiçbirini bilemezdi .

Bu modüllerin sembol tanımlarını tanıtan başlık dosyalarının olduğunu anlamak önemlidir; bir kez gerçekleştiğinde, çoklu kapanımların sembollerin yeniden tanımlanmasına (hatalara neden olan) neden olabileceği mantıklıdır, bu nedenle bu tür yeniden tanımlamaları önlemek için korumaları kullanıyoruz.


0

çünkü Headerfiles sınıfın ne içerdiğini tanımlar (Üyeler, veri yapıları) ve cpp dosyaları onu uygular.

Ve elbette, bunun ana nedeni, bir .h dosyasını diğer .h dosyalarına birden çok kez ekleyebilmenizdir, ancak bu, geçersiz olan bir sınıfın birden fazla tanımıyla sonuçlanır.

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.