Saat membuat perpustakaan kelas di C ++, Anda dapat memilih antara perpustakaan dinamis ( .dll
, .so
) dan statis ( .lib
, .a
). Apa perbedaan antara mereka dan kapan tepat untuk menggunakannya?
Saat membuat perpustakaan kelas di C ++, Anda dapat memilih antara perpustakaan dinamis ( .dll
, .so
) dan statis ( .lib
, .a
). Apa perbedaan antara mereka dan kapan tepat untuk menggunakannya?
Jawaban:
Perpustakaan statis menambah ukuran kode dalam biner Anda. Mereka selalu dimuat dan versi kode apa pun yang Anda kompilasi adalah versi kode yang akan dijalankan.
Perpustakaan dinamis disimpan dan diversi secara terpisah. Versi perpustakaan dinamis mungkin dimuat yang bukan yang asli yang dikirimkan dengan kode Anda jika pembaruan dianggap biner kompatibel dengan versi asli.
Selain itu perpustakaan dinamis tidak selalu dimuat - biasanya dimuat saat pertama kali dipanggil - dan dapat dibagi di antara komponen yang menggunakan perpustakaan yang sama (beberapa data dimuat, satu memuat kode).
Perpustakaan dinamis dianggap sebagai pendekatan yang lebih baik sebagian besar waktu, tetapi awalnya mereka memiliki kelemahan besar (google DLL hell), yang semuanya telah dieliminasi oleh OS Windows yang lebih baru (khususnya Windows XP).
Yang lain cukup menjelaskan apa itu perpustakaan statis, tetapi saya ingin menunjukkan beberapa peringatan menggunakan perpustakaan statis, setidaknya di Windows:
Lajang: Jika sesuatu harus bersifat global / statis dan unik, berhati-hatilah untuk meletakkannya di perpustakaan statis. Jika beberapa DLL ditautkan dengan perpustakaan statis itu, mereka masing-masing akan mendapatkan salinan singleton mereka sendiri. Namun, jika aplikasi Anda adalah EXE tunggal tanpa DLL khusus, ini mungkin tidak menjadi masalah.
Penghapusan kode yang tidak direferensikan : Ketika Anda menautkan ke perpustakaan statis, hanya bagian-bagian dari perpustakaan statis yang dirujuk oleh DLL / EXE Anda yang akan ditautkan ke DLL / EXE Anda.
Misalnya, jika hanya mylib.lib
berisi a.obj
dan b.obj
dan DLL / EXE Anda hanya mereferensikan fungsi atau variabel a.obj
, keseluruhannya b.obj
akan dibuang oleh tautan. Jika b.obj
berisi objek global / statis, konstruktor dan penghancurnya tidak akan dieksekusi. Jika konstruktor / destruktor tersebut memiliki efek samping, Anda mungkin kecewa dengan ketidakhadiran mereka.
Demikian juga, jika pustaka statis berisi titik masuk khusus Anda mungkin perlu berhati-hati bahwa mereka benar-benar disertakan. Contoh dari ini dalam pemrograman tertanam (oke, bukan Windows) akan menjadi pengendali interupsi yang ditandai sebagai alamat tertentu. Anda juga harus menandai interrupt handler sebagai titik masuk untuk memastikannya tidak dibuang.
Konsekuensi lain dari ini adalah bahwa pustaka statis dapat berisi file objek yang benar-benar tidak dapat digunakan karena referensi yang tidak terselesaikan, tetapi itu tidak akan menyebabkan kesalahan linker sampai Anda mereferensikan fungsi atau variabel dari file objek tersebut. Ini mungkin terjadi lama setelah perpustakaan ditulis.
Simbol debug: Anda mungkin ingin PDB terpisah untuk setiap pustaka statis, atau Anda mungkin ingin simbol debug ditempatkan di file objek sehingga mereka bisa digulirkan ke PDB untuk DLL / EXE. Dokumentasi Visual C ++ menjelaskan opsi yang diperlukan .
RTTI: Anda mungkin berakhir dengan banyak type_info
objek untuk kelas yang sama jika Anda menautkan pustaka statis tunggal ke beberapa DLL. Jika program Anda menganggap bahwa itu type_info
adalah data "tunggal" dan penggunaan &typeid()
atau type_info::before()
, Anda mungkin mendapatkan hasil yang tidak diinginkan dan mengejutkan.
Lib adalah unit kode yang dibundel dalam aplikasi Anda yang dapat dieksekusi.
Dll adalah unit mandiri dari kode yang dapat dieksekusi. Itu dimuat dalam proses hanya ketika panggilan dibuat ke dalam kode itu. Dll dapat digunakan oleh banyak aplikasi dan dimuat dalam banyak proses, sementara hanya memiliki satu salinan kode pada hard drive.
Kelebihan Dll : dapat digunakan untuk menggunakan kembali / berbagi kode antara beberapa produk; memuat dalam memori proses sesuai permintaan dan dapat dibongkar saat tidak diperlukan; dapat ditingkatkan secara terpisah dari sisa program.
Kontra Dll : dampak kinerja pemuatan dll dan rebasing kode; masalah versi ("dll neraka")
Pro lib : tidak ada dampak kinerja karena kode selalu dimuat dalam proses dan tidak diubah; tidak ada masalah versi.
Lib cons : executable / process "bloat" - semua kode ada di executable Anda dan dimuat saat proses dimulai; no reuse / sharing - setiap produk memiliki salinan kodenya sendiri.
Selain implikasi teknis dari pustaka statis vs dinamis (file statis membundel semuanya dalam satu pustaka biner vs dinamis yang memungkinkan berbagi kode di antara beberapa executable yang berbeda), ada implikasi hukumnya .
Misalnya, jika Anda menggunakan kode berlisensi LGPL dan Anda menautkan secara statis terhadap perpustakaan LGPL (dan dengan demikian membuat satu biner besar), kode Anda secara otomatis menjadi Open Source (kode bebas kebebasan) kode LGPL. Jika Anda menautkan ke objek yang dibagikan, maka Anda hanya perlu LGPL perbaikan / perbaikan bug yang Anda buat ke perpustakaan LGPL itu sendiri.
Ini menjadi masalah yang jauh lebih penting jika Anda memutuskan bagaimana mengompilasi aplikasi seluler Anda misalnya (di Android Anda memiliki pilihan statis vs dinamis, di iOS Anda tidak - selalu statis).
Program C ++ dibangun dalam dua fase
Pustaka statis (.lib) hanyalah kumpulan file .obj dan karenanya bukan program yang lengkap. Itu belum mengalami tahap kedua (menghubungkan) membangun program. DLL, di sisi lain, seperti exe dan karenanya adalah program yang lengkap.
Jika Anda membangun pustaka statis, itu belum ditautkan dan oleh karena itu konsumen pustaka statis Anda harus menggunakan kompiler yang sama dengan yang Anda gunakan (jika Anda menggunakan g ++, mereka harus menggunakan g ++).
Jika Anda membangun dll (dan membuatnya dengan benar ), Anda telah membangun program lengkap yang dapat digunakan oleh semua konsumen, tidak peduli kompiler mana yang mereka gunakan. Ada beberapa batasan meskipun, pada ekspor dari dll, jika kompatibilitas kompiler silang diinginkan.
consumers of your static library will have to use the same compiler that you used
jika perpustakaan statis menggunakan perpustakaan C ++, seperti #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]>
Pustaka statis dikompilasi ke dalam klien. .Lib digunakan pada waktu kompilasi dan isi pustaka menjadi bagian dari executable yang dikonsumsi.
Pustaka dinamis dimuat saat runtime dan tidak dikompilasi ke dalam klien yang dapat dieksekusi. Pustaka dinamis lebih fleksibel karena banyak klien yang dapat dieksekusi dapat memuat DLL dan memanfaatkan fungsinya. Ini juga menjaga ukuran keseluruhan dan rawatan kode klien Anda ke minimum.
Anda harus memikirkan dengan hati-hati tentang perubahan dari waktu ke waktu, versi, stabilitas, kompatibilitas, dll.
Jika ada dua aplikasi yang menggunakan kode bersama, apakah Anda ingin memaksa aplikasi tersebut untuk berubah bersama, jika mereka harus kompatibel satu sama lain? Kemudian gunakan dll. Semua exe akan menggunakan kode yang sama.
Atau apakah Anda ingin mengisolasi mereka dari satu sama lain, sehingga Anda dapat mengubah satu dan yakin Anda tidak melanggar yang lain. Kemudian gunakan lib statis.
DLL neraka adalah ketika Anda mungkin HARUS menggunakan lib statis, tetapi Anda menggunakan dll sebagai gantinya, dan tidak semua ongkos comaptible dengan itu.
Perpustakaan statis harus ditautkan ke executable akhir; itu menjadi bagian dari executable dan mengikutinya ke mana pun ia pergi. Pustaka dinamis dimuat setiap kali executable dieksekusi dan tetap terpisah dari executable sebagai file DLL.
Anda akan menggunakan DLL ketika Anda ingin dapat mengubah fungsi yang disediakan oleh perpustakaan tanpa harus menautkan kembali yang dapat dieksekusi (cukup ganti file DLL, tanpa harus mengganti file yang dapat dieksekusi).
Anda akan menggunakan perpustakaan statis setiap kali Anda tidak memiliki alasan untuk menggunakan perpustakaan dinamis.
Makalah Ulrich Drepper tentang " Bagaimana Menulis Perpustakaan yang Dibagi " juga merupakan sumber yang bagus yang merinci cara terbaik untuk memanfaatkan perpustakaan bersama, atau apa yang ia sebut sebagai "Objek Bersama Dinamis" (DSO). Ini lebih berfokus pada perpustakaan bersama dalam format biner ELF , tetapi beberapa diskusi juga cocok untuk Windows DLL.
Untuk diskusi yang sangat baik tentang topik ini, baca artikel ini dari Sun.
Ini masuk ke semua manfaat termasuk bisa memasukkan perpustakaan interposing. Rincian lebih lanjut tentang penempatan dapat ditemukan di artikel ini di sini .
Benar-benar trade off yang Anda buat (dalam proyek besar) adalah pada waktu pemuatan awal, perpustakaan akan dihubungkan pada satu waktu atau yang lain, keputusan yang harus dibuat adalah apakah tautan tersebut akan memakan waktu cukup lama yang dibutuhkan oleh kompilator untuk menggigit peluru dan melakukannya di depan, atau dapatkah penghubung dinamis melakukannya pada waktu pengambilan.
Jika pustaka Anda akan dibagikan di antara beberapa file yang dapat dieksekusi, seringkali masuk akal untuk membuatnya dinamis untuk mengurangi ukuran file yang dapat dieksekusi. Kalau tidak, pasti membuatnya statis.
Ada beberapa kelemahan menggunakan dll. Ada overhead tambahan untuk memuat dan menurunkannya. Ada juga ketergantungan tambahan. Jika Anda mengubah dll untuk membuatnya tidak kompatibel dengan eksekutif Anda, mereka akan berhenti bekerja. Di sisi lain, jika Anda mengubah pustaka statis, file executable Anda yang dikompilasi menggunakan versi lama tidak akan terpengaruh.
Jika pustaka statis, maka pada saat tautan kode terhubung dengan executable Anda. Ini membuat executable Anda lebih besar (daripada jika Anda pergi rute dinamis).
Jika pustaka itu dinamis maka pada tautan waktu referensi ke metode yang diperlukan dibangun untuk dieksekusi Anda. Ini berarti Anda harus mengirimkan perpustakaan Anda yang dapat dieksekusi dan dinamis. Anda juga harus mempertimbangkan apakah akses bersama ke kode di perpustakaan aman, lebih disukai memuat alamat di antara hal-hal lain.
Jika Anda bisa hidup dengan perpustakaan statis, pergi dengan perpustakaan statis.
Kami menggunakan banyak DLL (> 100) dalam proyek kami. DLL ini memiliki ketergantungan satu sama lain dan oleh karena itu kami memilih pengaturan tautan dinamis. Namun memiliki kelemahan sebagai berikut:
Mungkin setup yang lebih baik adalah menjadikan semuanya perpustakaan statis (dan karena itu Anda hanya memiliki satu executable). Ini hanya berfungsi jika tidak ada duplikasi kode yang terjadi. Sebuah tes tampaknya mendukung asumsi ini, tetapi saya tidak dapat menemukan kutipan MSDN resmi. Jadi misalnya membuat 1 exe dengan:
Kode dan variabel shared_lib2 harus ada di final yang dapat dieksekusi hanya sekali. Adakah yang bisa mendukung pertanyaan ini?
Pustaka statis adalah arsip yang berisi kode objek untuk pustaka, ketika ditautkan ke aplikasi yang dikompilasi ke dalam kode yang dapat dieksekusi. Pustaka bersama berbeda karena tidak dikompilasi ke dalam executable. Sebagai gantinya linker dinamis mencari beberapa direktori untuk mencari perpustakaan yang dibutuhkan, lalu memuatnya ke dalam memori. Lebih dari satu yang dapat dieksekusi dapat menggunakan pustaka bersama yang sama pada saat yang sama, sehingga mengurangi penggunaan memori dan ukuran yang dapat dieksekusi. Namun, ada lebih banyak file untuk didistribusikan dengan executable. Anda perlu memastikan bahwa perpustakaan diinstal ke sistem penggunaan di suatu tempat di mana linker dapat menemukannya, tautan statis menghilangkan masalah ini tetapi menghasilkan file yang dapat dieksekusi lebih besar.
Jika pekerjaan Anda pada proyek yang disematkan atau platform khusus perpustakaan statis adalah satu-satunya cara untuk pergi, sering kali mereka tidak terlalu repot untuk dikompilasi ke dalam aplikasi Anda. Juga memiliki proyek dan makefile yang mencakup semuanya membuat hidup lebih bahagia.
Saya akan memberikan aturan umum bahwa jika Anda memiliki basis kode yang besar, semua dibangun di atas perpustakaan tingkat rendah (misalnya kerangka kerja Utils atau Gui), yang ingin Anda partisi menjadi perpustakaan yang lebih mudah dikelola kemudian menjadikannya perpustakaan statis. Pustaka dinamis tidak benar-benar membelikan Anda apa-apa dan ada lebih sedikit kejutan - hanya akan ada satu contoh lajang misalnya.
Jika Anda memiliki pustaka yang sepenuhnya terpisah dengan basis kode lainnya (mis. Pustaka pihak ketiga) maka pertimbangkan untuk menjadikannya sebagai dll. Jika pustaka adalah LGPL, Anda mungkin harus menggunakan dll karena kondisi lisensi.