C ++ 'da bir sınıf kütüphanesi oluştururken, dinamik ( .dll
, .so
) ve statik ( .lib
, .a
) kütüphaneler arasında seçim yapabilirsiniz . Aralarındaki fark nedir ve hangisini kullanmak ne zaman uygundur?
C ++ 'da bir sınıf kütüphanesi oluştururken, dinamik ( .dll
, .so
) ve statik ( .lib
, .a
) kütüphaneler arasında seçim yapabilirsiniz . Aralarındaki fark nedir ve hangisini kullanmak ne zaman uygundur?
Yanıtlar:
Statik kütüphaneler, ikili kodunuzdaki kodun boyutunu artırır. Her zaman yüklenir ve derlediğiniz kodun herhangi bir sürümü çalışacak kodun sürümüdür.
Dinamik kütüphaneler ayrı olarak saklanır ve versiyonlanır. Güncellemenin orijinal sürümle ikili olarak uyumlu olduğu kabul edilirse , dinamik kitaplığın bir sürümünün kodunuzla birlikte gönderilen orijinal olmayan yüklenmesi mümkündür .
Ayrıca, dinamik kitaplıklar mutlaka yüklenmez - genellikle ilk çağrıldığında yüklenir - ve aynı kitaplığı kullanan bileşenler arasında paylaşılabilir (birden çok veri yükü, bir kod yükü).
Dinamik kütüphaneler çoğu zaman daha iyi bir yaklaşım olarak kabul edildi, ancak başlangıçta daha yeni Windows işletim sistemleri (özellikle Windows XP) tarafından ortadan kaldırılan ancak büyük bir kusurları (google DLL cehennemi) vardı.
Diğerleri statik bir kitaplığın ne olduğunu yeterince açıkladı, ancak en azından Windows'da statik kitaplık kullanmanın bazı uyarılarına dikkat çekmek istiyorum:
Singletons: Bir şeyin global / statik ve benzersiz olması gerekiyorsa, statik bir kütüphaneye koymaya çok dikkat edin. Bu statik kütüphaneye birden fazla DLL bağlıysa, her biri kendi singleton kopyasını alır. Ancak, uygulamanız hiçbir özel DLL ile tek bir EXE ise, bu bir sorun olmayabilir.
Referans dışı kod kaldırma: Statik bir kütüphaneye bağlandığınızda , yalnızca statik kütüphanenin DLL / EXE tarafından başvurulan kısımları DLL / EXE'nize bağlanacaktır.
Örneğin, eğer mylib.lib
içeren a.obj
ve b.obj
ve DLL / EXE dosyalarınız yalnızca işlevlere veya değişkenlere başvuruyorsa a.obj
, tamamı b.obj
bağlayıcı tarafından atılır. Eğer b.obj
küresel / statik nesneleri içeren, kendi kurucular ve yokediciler idam olmaz. Bu kurucuların / yıkıcıların yan etkileri varsa, yokluklarından hayal kırıklığına uğrayabilirsiniz.
Benzer şekilde, statik kitaplık özel giriş noktaları içeriyorsa, bunların gerçekten dahil edilmesine dikkat etmeniz gerekebilir. Gömülü programlamada bunun bir örneği (tamam, Windows değil), belirli bir adreste olduğu belirtilen bir kesme işleyicisi olabilir. Ayrıca, atılmadığından emin olmak için kesme işleyicisini bir giriş noktası olarak işaretlemeniz gerekir.
Bunun bir başka sonucu, statik bir kitaplığın, çözümlenemeyen başvurular nedeniyle tamamen kullanılamayan nesne dosyaları içerebilmesidir, ancak bu nesne dosyalarından bir işleve veya değişkene başvuru yapana kadar bir bağlayıcı hatasına neden olmaz. Bu kütüphane yazıldıktan çok sonra olabilir.
Hata ayıklama sembolleri: Her statik kitaplık için ayrı bir PDB isteyebilirsiniz veya hata ayıklama simgelerinin nesne dosyalarına yerleştirilmesini ve böylece DLL / EXE için PDB'ye aktarılmasını isteyebilirsiniz. Visual C ++ belgeleri gerekli seçenekleri açıklar .
RTTI:type_info
Tek bir statik kitaplığı birden çok DLL'ye bağlarsanız , aynı sınıf için birden çok nesne kullanabilirsiniz. Programınız bunun type_info
"singleton" verisi olduğunu varsayarsa &typeid()
veya veya kullanırsa type_info::before()
, istenmeyen ve şaşırtıcı sonuçlar alabilirsiniz.
Bir lib, uygulamanızın yürütülebilir dosyası içinde paketlenmiş bir kod birimidir.
Bir dll bağımsız bir yürütülebilir kod birimidir. Bu koda yalnızca bir çağrı yapıldığında sürece yüklenir. Bir dll, birden çok uygulama tarafından kullanılabilir ve sabit sürücüde kodun yalnızca bir kopyasına sahipken birden çok işlemde yüklenebilir.
Dll artıları : birkaç ürün arasında kodu yeniden kullanmak / paylaşmak için kullanılabilir; talep üzerine işlem belleğine yüklenir ve ihtiyaç duyulmadığında boşaltılabilir; programın geri kalanından bağımsız olarak yükseltilebilir.
Dll eksileri : dll yükleme ve kod yeniden basma performans etkisi; sürüm sorunları ("dll cehennem")
Lib artıları : Kod her zaman süreçte yüklendiğinden ve yeniden temel alınmadığından performans etkisi yoktur; sürüm sorunu yok.
Lib eksileri : yürütülebilir / süreç "bloat" - tüm kod yürütülebilir dosyasındadır ve işlem başladıktan sonra yüklenir; yeniden kullanım / paylaşım yok - her ürünün kendi kod kopyası vardır.
Statik ve dinamik kitaplıkların teknik sonuçlarının yanı sıra (statik dosyalar, birçok farklı yürütülebilir dosya arasında kod paylaşımına izin veren bir büyük ikili ve dinamik kitaplıklardaki her şeyi bir araya getirir), yasal sonuçları vardır .
Örneğin, LGPL lisanslı kod kullanıyorsanız ve statik olarak bir LGPL kitaplığına bağlarsanız (ve böylece bir büyük ikili dosya oluşturursanız), kodunuz otomatik olarak Açık Kaynaklı ( özgürlükte olduğu gibi ücretsiz) LGPL kodu haline gelir . Paylaşılan nesnelere bağlanırsanız, LGPL kitaplığının kendisine yaptığınız iyileştirmeleri / hata düzeltmelerini yapmanız yeterlidir.
Örneğin mobil uygulamalarınızı nasıl derleyeceğinize karar verirseniz, bu çok daha önemli bir sorun haline gelir (Android'de statik ve dinamik seçenekleriniz vardır, iOS'ta yoktur - her zaman statiktir).
C ++ programları iki aşamada oluşturulmuştur
Statik kütüphane (.lib) sadece .obj dosyalarının bir paketidir ve bu nedenle tam bir program değildir. Bir program oluşturmanın ikinci (bağlantı) aşamasına geçmedi. Dll, diğer taraftan, exe gibidir ve bu nedenle tam programlar.
Statik bir kitaplık oluşturursanız, henüz bağlanmamıştır ve bu nedenle statik kitaplığınızın tüketicileri kullandığınızla aynı derleyiciyi kullanmalıdır (g ++ kullandıysanız, g ++ kullanmaları gerekir).
Bunun yerine bir dll oluşturduysanız (ve doğru şekilde oluşturduysanız ), hangi derleyiciyi kullanıyor olursa olsun, tüm tüketicilerin kullanabileceği eksiksiz bir program oluşturdunuz. Çapraz derleyici uyumluluğu isteniyorsa, bir dll dışa aktarma konusunda çeşitli kısıtlamalar vardır.
consumers of your static library will have to use the same compiler that you used
statik kitaplık gibi C ++ kitaplığı kullanıyorsa #include <iostream>
.
$$:~/static [32]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/static [33]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H
void foo();
#endif
$$:~/static [34]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/static [35]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H
void foo2();
#endif
$$:~/static [36]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/static [37]> cat makefile
hello: hello.o libtest.a
cc -o hello hello.o -L. -ltest
hello.o: hello.c
cc -c hello.c -I`pwd`
libtest.a:foo.o foo2.o
ar cr libtest.a foo.o foo2.o
foo.o:foo.c
cc -c foo.c
foo2.o:foo.c
cc -c foo2.c
clean:
rm -f foo.o foo2.o libtest.a hello.o
$$:~/static [38]>
$$:~/dynamic [44]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/dynamic [45]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H
void foo();
#endif
$$:~/dynamic [46]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/dynamic [47]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H
void foo2();
#endif
$$:~/dynamic [48]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/dynamic [49]> cat makefile
hello:hello.o libtest.sl
cc -o hello hello.o -L`pwd` -ltest
hello.o:
cc -c -b hello.c -I`pwd`
libtest.sl:foo.o foo2.o
cc -G -b -o libtest.sl foo.o foo2.o
foo.o:foo.c
cc -c -b foo.c
foo2.o:foo.c
cc -c -b foo2.c
clean:
rm -f libtest.sl foo.o foo
2.o hello.o
$$:~/dynamic [50]>
Statik bir kütüphane istemciye derlenir. Derleme sırasında bir .lib kullanılır ve kitaplığın içeriği tüketilen yürütülebilir dosyanın bir parçası haline gelir.
Dinamik bir kütüphane çalışma zamanında yüklenir ve istemci yürütülebilir dosyasına derlenmez. Birden çok istemci yürütülebilir dosyası bir DLL dosyasını yükleyebildiğinden ve işlevselliğini kullanabildiğinden dinamik kitaplıklar daha esnektir. Bu, istemci kodunuzun genel boyutunu ve sürdürülebilirliğini de minimumda tutar.
Zaman içindeki değişiklikler, sürüm oluşturma, kararlılık, uyumluluk vb. Hakkında dikkatlice düşünmelisiniz.
Paylaşılan kodu kullanan iki uygulama varsa, birbirleriyle uyumlu olmaları gerektiğinde bu uygulamaları birlikte değiştirmeye zorlamak ister misiniz? Sonra dll kullanın. Tüm exe'ler aynı kodu kullanacaktır.
Yoksa bunları birbirinden izole etmek mi istiyorsunuz, böylece birini değiştirebilir ve diğerini kırmadığınızdan emin olabilirsiniz. Ardından statik lib kullanın.
DLL cehennem muhtemelen statik bir lib kullanmış olmalısınız, ancak bunun yerine bir dll kullandınız ve tüm exes onunla comaptible değildir.
Statik bir kütüphane son çalıştırılabilir dosyaya bağlanmalıdır; çalıştırılabilir dosyanın bir parçası olur ve gittiği her yerde onu takip eder. Yürütülebilir dosya yürütüldüğünde dinamik kitaplık yüklenir ve yürütülebilir dosyadan DLL dosyası olarak ayrı kalır.
Yürütülebilir dosyayı yeniden bağlamak zorunda kalmadan kitaplık tarafından sağlanan işlevselliği değiştirmek istediğinizde bir DLL kullanabilirsiniz (yürütülebilir dosyayı değiştirmek zorunda kalmadan yalnızca DLL dosyasını değiştirin).
Dinamik kitaplık kullanma nedeniniz olmadığında statik kitaplık kullanırsınız.
Ulrich Drepper'ın " Paylaşılan Kitaplıkların Yazılması" konulu makalesi de, paylaşılan kitaplıklardan en iyi nasıl yararlanılacağını veya "Dinamik Paylaşılan Nesneler" (DSO) olarak adlandırdığı şeyleri ayrıntılarıyla anlatan iyi bir kaynaktır. Daha çok ELF ikili biçimindeki paylaşılan kitaplıklara odaklanır , ancak bazı tartışmalar Windows DLL'leri için de uygundur.
Bu konunun mükemmel bir tartışması için Sun'ın bu makalesini okuyun .
Araya giren kütüphaneleri ekleyebilmek de dahil olmak üzere tüm avantajlara girer. İnterposing ile ilgili daha fazla bilgiyi bu makalede bulabilirsiniz .
Gerçekten yaptığınız işlem (büyük bir projede) ilk yükleme süresinde, kütüphaneler bir seferde birbirine bağlanacak, yapılması gereken karar, derleyicinin ihtiyaç duyduğu kadar uzun sürecek bağlantı mermi ısırmak ve önden yapmak için, ya da dinamik bağlayıcı bunu yükleme zamanında yapabilir.
Kitaplığınız birkaç yürütülebilir dosya arasında paylaşılacaksa, yürütülebilir dosyaların boyutunu azaltmak için dinamik hale getirmek genellikle mantıklıdır. Aksi takdirde, kesinlikle statik hale getirin.
Bir dll kullanmanın birkaç dezavantajı vardır. Yükleme ve boşaltma için ek yük vardır. Ayrıca ek bir bağımlılık var. Eğer dll executalbes ile uyumsuz yapmak için değiştirirseniz, onlar çalışma durur. Öte yandan, statik bir kitaplığı değiştirirseniz, eski sürümü kullanan derlenmiş yürütülebilir dosyalarınız etkilenmez.
Kütüphane statik ise, bağlantı sırasında kod çalıştırılabilir öğenize bağlanır. Bu, yürütülebilir dosyanızı büyütür (dinamik rotaya gittiğinizden daha).
Kütüphane dinamikse, bağlantı sırasında gerekli yöntemlere yapılan başvurular yürütülebilir dosyaya yerleştirilir. Bu, yürütülebilir dosyanızı ve dinamik kitaplığı göndermeniz gerektiği anlamına gelir. Ayrıca, kitaplıktaki koda paylaşılan erişimin güvenli, tercih edilen yükleme adresi olup olmadığını da göz önünde bulundurmalısınız.
Statik kitaplıkla yaşayabiliyorsanız, statik kitaplığa gidin.
Projemizde çok sayıda DLL (> 100) kullanıyoruz. Bu DLL'lerin birbirlerine bağımlılıkları vardır ve bu nedenle dinamik bağlantı kurulumunu seçtik. Bununla birlikte, aşağıdaki dezavantajlara sahiptir:
Belki daha iyi bir kurulum, her şeyi statik bir kütüphane yapmaktı (ve bu nedenle sadece bir yürütülebilir dosya var). Bu, yalnızca kod çoğaltma yapılmazsa çalışır. Bir test bu varsayımı destekliyor gibi görünüyor, ancak resmi bir MSDN teklifi bulamadım. Yani örneğin 1 exe yapın:
Shared_lib2 kodu ve değişkenleri son birleştirilmiş yürütülebilir dosyada yalnızca bir kez bulunmalıdır. Bu soruyu kimse destekleyebilir mi?
Statik kitaplıklar, kodun yürütülebilir dosyaya derlendiği bir uygulamaya bağlandığında kitaplığın nesne kodunu içeren arşivlerdir. Paylaşılan kitaplıklar, yürütülebilir dosyada derlenmemesi bakımından farklıdır. Bunun yerine dinamik bağlayıcı, ihtiyaç duyduğu kütüphaneyi / kütüphaneleri arayan bazı dizinleri arar ve bunu belleğe yükler. Birden fazla yürütülebilir dosya aynı anda aynı paylaşılan kütüphaneyi kullanabilir ve böylece bellek kullanımını ve yürütülebilir boyutu azaltabilir. Ancak, yürütülebilir dosyayla birlikte dağıtılacak daha fazla dosya vardır. Kütüphanenin, bağlayıcının bulabileceği bir yerde kullanım sistemine yüklendiğinden emin olmanız gerekir, statik bağlantı bu sorunu ortadan kaldırır, ancak daha büyük bir yürütülebilir dosyaya neden olur.
Gömülü projeler veya özel platformlar üzerinde çalışıyorsanız, statik kütüphaneler tek yoluysa, çoğu zaman uygulamanızda derleme zorluğu da azalır. Ayrıca, her şeyi içeren projeler ve makefile sahip olmak hayatı mutlu eder.
Genel bir kural olarak, tümünün daha düşük düzeyli kitaplıkların (örn. Utils veya Gui çerçevesi) üzerine kurulmuş, daha sonra yönetilebilir kitaplıklara bölmek ve bunları statik kitaplıklar haline getirmek istediğinizde genel bir kural veririm. Dinamik kütüphaneler size gerçekten hiçbir şey satın almaz ve daha az sürpriz vardır - örneğin yalnızca bir tane singleton örneği olacaktır.
Eğer tamamen kod temeli geri kalanı için ayrı bir kütüphane (örneğin bir üçüncü taraf kütüphane) varsa o zaman bir dll yapmayı düşünün. Kütüphane LGPL ise, lisans koşulları nedeniyle yine de bir dll kullanmanız gerekebilir.