Transitüel olarak dahil edilen başlıklara güvenmek iyi bir uygulama mıdır?


37

Üzerinde çalışmakta olduğum bir C ++ projesinde yer alan içerikleri temizliyorum ve doğrudan belirli bir dosyaya doğrudan kullanılan tüm başlıkları dahil edip etmemem gerektiğini veya sadece en düşük değeri içermemem gerektiğini merak ediyorum.

İşte bir örnek Entity.hpp:

#include "RenderObject.hpp"
#include "Texture.hpp"

struct Entity {
    Texture texture;
    RenderObject render();
}

(Bir ileriye yönelik bildirimin RenderObjectbir seçenek olmadığını varsayalım .)

Şimdi, bunun RenderObject.hppiçerdiğini biliyorum Texture.hpp- bunu biliyorum çünkü her RenderObjectbirinin bir Textureüyesi var. Yine de açıkça katılıyorum çünkü Texture.hppiçerisine Entity.hppgüvenmenin iyi bir fikir olup olmadığından emin değilim RenderObject.hpp.

Yani: İyi bir uygulama mı, değil mi?


19
Örnekteki güvenlik görevlileri nerede? Yanlışlıkla onları unuttun mu umarım?
Doktor Brown

3
Kullanılan tüm dosyaları dahil etmediğinizde ortaya çıkan bir sorun, bazen dosyaları eklediğiniz sıranın önemli hale gelmesidir. Bu, olayın gerçekleştiği tek davada gerçekten can sıkıcı bir durum, ancak bazen kartopu ve sonunda böyle bir kod yazan kişiyi, ateş eden bir ekibin önünde yürüdüğünü dileğiyle.
Dunk,

Bu yüzden var #ifndef _RENDER_H #define _RENDER_H ... #endif.
sampathsris

@Dunk Sorunu yanlış anladığınızı düşünüyorum. Olmaması gereken önerileri ile.
Mooing Duck

1
@DocBrown, halleder, tamam #pragma oncemı?
Pacerier

Yanıtlar:


65

.Cpp dosyasında kullanılan herhangi bir nesneyi tanımlayan tüm başlıkları, bu dosyalarda ne olduğunu bildiğinizden bağımsız olarak o dosyaya dahil etmelisiniz. Başlıkları birkaç kez dahil etmenin önemli olmadığından emin olmak için tüm başlık dosyalarına korumalar eklemelisiniz.

Nedenler:

  • Bu, kaynağı tam olarak söz konusu kaynak dosyasının gerektirdiğini okuyan geliştiricilere açıkça belirtir. Burada, dosyadaki ilk birkaç satıra bakan biri Texture, bu dosyadaki nesnelerle ilgilendiğinizi görebilir .
  • Bu, yeniden yapılandırılmış başlıkların artık belirli başlıklara ihtiyaç duymadıklarında derleme sorunlarına neden olduğu sorunlardan kaçınır. Örneğin RenderObject.hpp, bunun aslında Texture.hppkendisine ihtiyaç duymadığını fark edersiniz .

Bunun bir sonucu olarak, bu dosyada açıkça gerekli olmadıkça, başka bir başlığa hiçbir başlık eklememelisiniz.


10
Sonuçlara katılıyorum - şartlı olarak, ihtiyaç duyarsa HER ZAMAN diğer başlıkları da içermelidir!
Andrew

1
Doğrudan bireysel derslerin başlıkları da dahil olmak üzere yapılan uygulamalardan hoşlanmıyorum. Ben kümülatif başlıkları tercih ediyorum. Diğer bir deyişle, üst düzey dosyanın kullandığı türden bir "modüle" başvurması gerektiğini düşünüyorum, ancak tüm parçaları doğrudan içermesi gerekmiyor.
edA-qa mort-ora-y 7:14

8
Bu, her dosyanın içerdiği çok küçük parçalara ihtiyaç duysalar bile, uzun derleme sürelerine neden olan ve yeniden düzenlemeyi zorlaştıran büyük, monolitik başlıklara yol açar.
Robot'a

6
Google, ne kullandığınızı da dahil olmak üzere bu tavsiyeyi tam olarak yerine getirmesine yardımcı olacak bir araç geliştirmiştir .
Matthew G.

3
Büyük yekpare başlıklı ana derleme zamanı, başlık kodunun derlenmesinin zamanı değil, uygulamanızdaki her cpp dosyasını, her başlık değiştiğinde derlemek zorunda kalıyor. Önceden derlenmiş başlıklar buna yardımcı olmaz.
Robot'a

23

Genel kural: Kullandığın şeyi dahil et. Doğrudan bir nesne kullanıyorsanız, başlık dosyasını doğrudan ekleyin. B'yi kullanan, ancak B'yi kendiniz kullanmayan A nesnesini kullanıyorsanız, yalnızca Ah

Ayrıca biz konudayken, başlığınıza gerçekten ihtiyaç duyduğunuzda başlığınızdaki diğer başlık dosyalarını eklemelisiniz. Yalnızca .cpp dosyasına ihtiyacınız varsa, o zaman yalnızca oraya ekleyin: bu, genel ve özel bir bağımlılık arasındaki farktır ve sınıfınızın kullanıcılarının gerçekten ihtiyaç duymadıkları başlıkları sürüklemesini önler.


10

Belli bir dosyaya doğrudan kullanılan tüm başlıkları açıkça eklemem gerekip gerekmediğini merak ediyorum.

Evet.

Diğer başlıkların ne zaman değişebileceğini asla bilemezsiniz. Her çeviri biriminde, çeviri biriminin ihtiyaç duyduğunu bildiğiniz başlıkları dahil etmek, dünyadaki tüm anlamlara gelir.

İkili eklemenin zararlı olmamasını sağlamak için başlık korumalarımız var.


3

Görüşler bu konuda farklı olsa da, her dosyanın (c / cpp kaynak dosyası veya h / hpp başlık dosyası) kendi başına derlenebilmesi veya analiz edilebilmesi gerektiği görüşündeyim.

Bu nedenle, tüm dosyalar ihtiyaç duydukları tüm üstbilgi dosyalarını içermelidir - bir üstbilgi dosyasının önceden eklenmiş olduğunu varsaymamalısınız.

Bir başlık dosyası eklemeniz ve başka bir yerde tanımlanmış olan ve doğrudan dahil etmeden tanımlanmış bir öğe kullandığını bulmanız gerekiyorsa, bu gerçekten acı vericidir ... bu yüzden bulmanız gerekir (ve muhtemelen yanlış olanı bulursunuz!)

Öte yandan, (genel bir kural olarak) ihtiyacınız olmayan bir dosya eklerseniz bunun bir önemi yoktur ...


Kişisel bir stil noktası olarak #include dosyalarını alfabetik sıraya göre düzenlerim, sisteme ve uygulamaya ayrılırım - bu, "kendi kendine yeten ve tamamen uyumlu" mesajını güçlendirmeye yardımcı olur.


İçeriğin sırası hakkında not: bazen X11 başlıkları dahil edildiğinde sıra önemlidir. Bu, tasarımdan kaynaklanabilir (bu durumda kötü tasarım olarak kabul edilebilir), bazen talihsiz uyumsuzluk sorunlarından kaynaklanabilir.
Hyde

Gereksiz başlıkları dahil etmeyle ilgili bir not, önce doğrudan derleme zamanları için önemlidir (özellikle şablon ağırlıklı C ++ ise), ancak özellikle içerme dosyasının da değiştiği, aynı veya bağımlılık projesinin başlıkları dahil edildiğinde ve bunların yeniden derlenmesini tetikleyeceği dahil her şey (eğer çalışma bağımlılığınız varsa, o zaman yapmazsanız her zaman temiz yapı yapmak zorundasınız…).
Hyde

2

Geçişli dahil edilmenin gerekliliğe (örneğin temel sınıf) veya bir uygulama detayına (özel üye) bağlı olup olmamasına bağlıdır.

Açıklığa kavuşturmak için, geçişin dahil edilmesi gerekir, ancak yalnızca orta başlıkta belirtilen arayüzleri değiştirdikten sonra yapılabilir. Bu zaten bir değişiklik olduğundan, onu kullanan herhangi bir .cpp dosyası zaten kontrol edilmelidir.

Örnek: Ah, C.cpp tarafından kullanılan Bh'ye dahil edilir. Eğer Bh, bazı uygulama detayları için Ah kullanıyorsa, C.cpp, Bh'nin yapmaya devam edeceğini varsaymamalıdır. Ancak eğer Bh bir temel sınıf için Ah kullanıyorsa, C.cpp, Bh'nin temel sınıfları için ilgili başlıkları dahil etmeye devam edeceğini varsayabilir.

Burada, başlık eklemeleri yinelenmeyen gerçek avantajını görüyorsunuz. Bh tarafından kullanılan temel sınıfın gerçekten Ah'a ait olmadığını ve Bh'nin kendisine yeniden yerleştirildiğini söyleyin. Bh şimdi bağımsız bir başlıktır. C.cpp gereksiz yere Ah dahilse, şimdi gereksiz bir başlık içerir.


2

Başka bir durum daha olabilir: Ah, Bh var ve senin C.cpp, Bh Ah içerir

yani C.cpp'de yazabilirsiniz

#include "B.h"
#include "A.h" // < this can be optional as B.h already has all the stuff in A.h

Peki burada #include "Ah" yazmazsanız ne olabilir? C.cpp'nizde, hem A hem de B (örneğin, sınıf) kullanılır. Daha sonra C.cpp kodunuzu değiştirdiniz, B ile ilişkili olanları kaldırın, ancak Bh orada bıraktı.

Hem Ah hem de Bh'yi ve şimdi bu noktaya dahil ederseniz, gereksiz kapsamları algılayan araçlar, Bh kapsamının artık gerekli olmadığını göstermenize yardımcı olabilir. Eğer sadece Bh'yi yukarıdaki gibi eklerseniz, kod değişimlerinden sonra gereksiz dahil etme araçlarını / insanı tespit etmek zordur.


1

Önerilen cevaplardan biraz daha farklı bir yaklaşım alıyorum.

Başlıklarda derleme geçişi yapmak için gereken her zaman yalnızca minimum düzeyde çıplak bir şekilde bulunur. Mümkün olan yerlerde ileriye dönük bildirim kullanın.

Kaynak dosyalarında, ne kadar içerdiğiniz önemli değildir. Tercihlerim hala geçmesi için minimum içermeyi hedefliyor.

Buradaki başlıklar dahil küçük projeler için fark yaratmaz. Ancak orta ila büyük projeler için sorun olabilir. En son donanım derlemek için kullanılsa bile, fark göze çarpıyor olabilir. Bunun nedeni, derleyicinin dahil edilen başlığı açması ve ayrıştırması gerektiğidir. Bu nedenle, yapıyı optimize etmek için yukarıdaki tekniği uygulayın (en düşük düzeyde kullanın ve ileriye yönelik beyan kullanın).

Biraz eski olmasına rağmen, Büyük Ölçekli C ++ Yazılım Tasarımı (John Lakos tarafından) tüm bunları ayrıntılı olarak açıklıyor.


1
Bu stratejiye katılmıyorum ... bir kaynak dosyaya bir başlık dosyası eklerseniz, tüm bağımlılıklarını izlemeniz gerekir. Listeyi denemek ve belgelemek yerine doğrudan eklemek daha iyidir!
Andrew,

@Ayrıca, neyin ve kaç defa dahil edildiğini kontrol etmek için araçlar ve komut dosyaları vardır.
BЈовић

1
Bununla başa çıkmak için en son derleyicilerden bazılarında optimizasyon olduğunu fark ettim. Tipik bir koruma ifadesini tanırlar ve işleme koyarlar. Ardından, # tekrar eklerken, dosya yükünü tamamen optimize edebilirler. Bununla birlikte, ileriye dönük beyannamelerin önerilmesi, içerme sayısını azaltmak için çok akıllıca olacaktır. İleriye dönük beyannameleri kullanmaya başladığınızda, her şirketin farklı bir denge kurduğu, derleyici çalışma zamanı (ileriye dönük beyannameler ile iyileştirilmiş) ve kullanıcı dostu (uygun ekstra kullanımlara göre geliştirilmiş) dengesi olur.
Cort Ammon

1
@CortAmmon Tipik bir başlıkta korumalar var, ancak derleyici hala açmalı ve bu yavaş işlem
BЈовић

4
@ BЈовић: Aslında yapmazlar. Tek yapmaları gereken, dosyanın "tipik" başlık korumalarına sahip olduğunu tanımak ve onları yalnızca bir kez açacak şekilde işaretlemek. Örneğin, Gcc,
Cort Ammon

-4

İyi uygulama, derlenmediği sürece başlık stratejiniz için endişelenmemenizdir.

Kodunuzun başlık kısmı, kolayca çözülen bir derleme hatası elde edene kadar kimsenin bakmaması gereken bir satır bloğudur. 'Doğru' üslubun arzusunu anlıyorum, ama hiçbir şekilde gerçekten doğru olarak tanımlanamaz. Her sınıf için bir başlık eklemek, rahatsız edici bir sıralamaya dayalı derleme hatalarına neden olma ihtimalinin daha yüksek olmasına karşın, bu derleme hatalarının, dikkatli kodlamanın çözebileceği sorunları da yansıtıyor (muhtemelen düzeltmek için zaman ayırmayacaklarsa da).

Ve evet, olur içine almaya başlamak kez bu düzen temelli problem friendarazi.

Sorunu iki durumda düşünebilirsiniz.


Durum 1: Bir düzineden az, birbirleriyle etkileşime giren az sayıda sınıfınız var. Bu başlıkları, birbirlerine bağımlılıklarını etkileyebilecek şekillerde düzenli olarak ekler, kaldırır ve başka şekillerde değiştirirsiniz. Kod örneğinizin önerdiği durum budur.

Başlık kümesi, kırpılan herhangi bir sorunu çözmenin karmaşık olmaması için yeterince küçüktür. Zor sorunlar, bir veya iki başlığın yeniden yazılması ile giderilir. Başlık stratejiniz hakkında endişelenmek, var olmayan sorunları çözmektir.


2. Durum: Onlarca sınıfınız var. Bazı sınıflar programınızın bel kemiğini temsil eder ve başlıklarının yeniden yazılması sizi kod tabanınızın büyük bir kısmını yeniden yazmaya / tekrar derlemeye zorlar. Diğer sınıflar bu omurgayı işleri başarmak için kullanır. Bu, tipik bir işletme ortamını temsil eder. Başlıklar, dizinlere yayılmıştır ve her şeyin adını gerçekçi bir şekilde hatırlayamazsınız.

Çözüm: Bu noktada, sınıflarınızı mantıksal gruplar halinde düşünmeniz ve bu grupları tekrar tekrar yapmak zorunda kalmamak için başlıklara ayırmanız gerekir #include. Bu sadece hayatı kolaylaştırmakla kalmaz, aynı zamanda önceden derlenmiş başlıklardan yararlanmak için gerekli bir adımdır .

İhtiyacın #includeolmayan ama kimin umrunda değil mi?

Bu durumda, kodunuz ...

#include <Graphics.hpp>

struct Entity {
    Texture texture;
    RenderObject render();
}

13
Bunu -1 ile yapmak zorundaydım çünkü dürüst olmak gerekirse "İyi uygulama, _______________________________________________________________________________________________________________________________________________________________________________________________________________ Kuzgun bir yargıya yol açarsa," İyi uygulama __________ stratejiniz için endişelenmek değildir "şeklinde olan bir cümlenin dürüstçe olduğuna inanıyorum. Yaklaşımın okunaksızlığa doğru çok hızlı yol açtığını ve okunmazlığın “işe yaramadığı kadar kötü” olduğunu gördüm. Ayrıca, açıkladığınız her iki durumun sonuçlarıyla aynı fikirde olmayan birçok büyük kütüphaneler buldum. Örnek olarak, Boost DOES, 2. durumda önerdiğiniz "koleksiyon" başlıklarını yapar, ancak ihtiyaç duyduğunuzda sınıflara göre sınıf başlıkları sağlama konusunda da büyük bir anlaşma yapar.
Cort Ammon

3
Şahsen "derlerseniz endişelenme" ye şahit oldum "uygulamasına bir enuma değer kattığınızda derlememiz 30 dakika sürüyor, bunu nasıl düzelteceğiz?"
Robot'a

Cevabımda derleme zamanı konusunu ele aldım. Aslında cevabım, sadece ikisinden (ikisi de iyi puan vermedi) biri. Fakat gerçekte, bu OP'nin sorununa teğet; bu, "Değişken isimlerimi devirmeli miyim?" soru yazın. Cevabımın popüler olmadığını farkettim, ama her zaman her şey için en iyi uygulama yoktur ve bu durumlardan biridir.
QuestionC

# 2 ile aynı fikirdeyim. Daha önceki fikirlere gelince - yerel başlık bloğunu güncelleyecek otomasyon için umut ediyorum - o zamana kadar tam bir listeyi savunuyorum.
chux - Monica

"Her şeyi ve mutfak lavabosunu dahil etme" yaklaşımı başlangıçta biraz zaman kazandırabilir - başlık dosyalarınız daha küçük görünebilir (çünkü çoğu şey dolaylı olarak ... bir yerden dahil edilmiştir). Herhangi bir yerde herhangi bir değişikliğin 30 + dakikadan itibaren projenizi yeniden derlemeye neden olduğu noktaya gelene kadar Ve IDE-smart otomatik tamamlama cihazınız yüzlerce alakasız öneri getiriyor. Ve yanlışlıkla benzer şekilde adlandırılmış iki sınıf veya statik işlev karıştırın. Ve yeni bir yapı eklersiniz ancak derleme başarısız olur, çünkü bir yerde tamamen ilgisiz bir sınıfla bir ad alanı çarpışması var ...
CharonX
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.