C ++에서 클래스 라이브러리를 작성할 때 동적 ( .dll
, .so
) 및 정적 ( .lib
, .a
) 라이브러리 중에서 선택할 수 있습니다 . 그들 사이의 차이점은 무엇이며 어느 것을 사용하는 것이 적절한가요?
C ++에서 클래스 라이브러리를 작성할 때 동적 ( .dll
, .so
) 및 정적 ( .lib
, .a
) 라이브러리 중에서 선택할 수 있습니다 . 그들 사이의 차이점은 무엇이며 어느 것을 사용하는 것이 적절한가요?
답변:
정적 라이브러리는 바이너리의 코드 크기를 증가시킵니다. 그것들은 항상로드되며 컴파일 한 코드의 버전은 실행될 코드의 버전입니다.
동적 라이브러리는 별도로 저장되고 버전이 지정됩니다. 업데이트가 원래 버전과 이진 호환되는 것으로 간주되는 경우 코드 와 함께 제공된 원래 버전 이 아닌 동적 라이브러리 버전을로드 할 수 있습니다 .
또한 동적 라이브러리는 반드시로드 할 필요는 없으며 일반적으로 처음 호출 될 때로드되며 동일한 라이브러리 (여러 데이터로드, 하나의 코드로드)를 사용하는 구성 요소간에 공유 할 수 있습니다.
동적 라이브러리는 대부분 더 나은 접근 방식으로 여겨졌지만 원래는 최신 Windows OS (특히 Windows XP)에서 제거 된 주요 결함 (Google DLL hell)이있었습니다.
다른 사람들은 정적 라이브러리가 무엇인지 적절하게 설명했지만 적어도 Windows에서 정적 라이브러리를 사용할 때의 몇 가지주의 사항을 지적하고 싶습니다.
싱글 톤 (Singleton) : 어떤 것이 전역 / 정적 / 고유적이고 독창적이어야하는 경우, 정적 라이브러리에 넣는 것에 매우주의하십시오. 여러 DLL이 해당 정적 라이브러리에 연결되어 있으면 각각 고유 한 단일 복사본을 얻게됩니다. 그러나 응용 프로그램이 사용자 지정 DLL이없는 단일 EXE 인 경우에는 문제가되지 않을 수 있습니다.
참조되지 않은 코드 제거 : 정적 라이브러리에 연결하면 DLL / EXE에서 참조하는 정적 라이브러리의 일부만 DLL / EXE에 연결됩니다.
예를 들어, mylib.lib
contains a.obj
및 b.obj
DLL / EXE가의 함수 또는 변수 만 참조 하는 경우 링커 a.obj
는 전체를 b.obj
버립니다. b.obj
전역 / 정적 객체가 포함되어 있으면 생성자와 소멸자가 실행되지 않습니다. 이러한 생성자 / 소멸자가 부작용이있는 경우 부재로 인해 실망 할 수 있습니다.
마찬가지로 정적 라이브러리에 특수 진입 점이 포함되어 있으면 실제로 포함되도록주의해야합니다. 임베디드 프로그래밍 (예 : Windows가 아님)에서이 예제는 특정 주소에있는 것으로 표시된 인터럽트 핸들러입니다. 또한 중단되지 않도록 인터럽트 처리기를 진입 점으로 표시해야합니다.
이것의 또 다른 결과는 정적 라이브러리가 해석되지 않은 참조로 인해 완전히 사용할 수없는 오브젝트 파일을 포함 할 수 있지만 해당 오브젝트 파일에서 함수 또는 변수를 참조 할 때까지 링커 오류를 발생시키지 않습니다. 이것은 라이브러리가 작성된 후 오래 지속될 수 있습니다.
디버그 기호 : 각 정적 라이브러리마다 별도의 PDB를 원하거나 DLL / EXE의 PDB로 롤백되도록 디버그 기호를 개체 파일에 배치 할 수 있습니다. Visual C ++ 문서는 필요한 옵션을 설명 합니다 .
RTTI :type_info
단일 정적 라이브러리를 여러 DLL에 연결하면 동일한 클래스에 대한 여러 개체 가 생길 수 있습니다 . 프로그램 type_info
이 "단일"데이터 인 것으로 가정 &typeid()
하거나 또는를 사용 type_info::before()
하면 바람직하지 않은 놀라운 결과를 얻을 수 있습니다.
lib는 응용 프로그램 실행 파일 내에 번들로 제공되는 코드 단위입니다.
dll은 실행 가능한 코드의 독립형 단위입니다. 해당 코드를 호출 할 때만 프로세스에로드됩니다. dll은 여러 응용 프로그램에서 사용할 수 있으며 여러 프로세스에로드되는 동시에 하드 드라이브에는 코드 사본이 하나만 남아 있습니다.
Dll 전문가 : 여러 제품간에 코드를 재사용 / 공유하는 데 사용할 수 있습니다. 요청시 프로세스 메모리에로드하고 필요하지 않은 경우 언로드 할 수 있습니다. 프로그램의 나머지 부분과 독립적으로 업그레이드 할 수 있습니다.
DLL 단점 : DLL 로딩 및 코드 리베이스의 성능 영향; 버전 관리 문제 ( "dll hell")
Lib 전문가 : 코드가 항상 프로세스에로드되고 리베이스되지 않으므로 성능에 영향을 미치지 않습니다. 버전 관리 문제가 없습니다.
Lib cons : executable / process "bloat"-모든 코드가 실행 파일에 있으며 프로세스 시작시로드됩니다. 재사용 / 공유 금지-각 제품마다 고유 한 코드 사본이 있습니다.
정적 대 동적 라이브러리의 기술적 의미 (정적 파일은 여러 다른 실행 파일간에 코드 공유를 허용하는 하나의 큰 바이너리 대 동적 라이브러리에 모든 것을 번들로 묶음) 외에도 법적 의미가 있습니다.
예를 들어 LGPL 라이센스 코드를 사용하고 있고 LGPL 라이브러리에 정적으로 링크하여 큰 바이너리를 생성하는 경우 코드는 자동으로 오픈 소스 ( 자유롭게 무료) LGPL 코드가됩니다. 공유 객체에 연결하는 경우 LGPL 라이브러리 자체의 개선 사항 / 버그 수정 만 LGPL하면됩니다.
예를 들어 모바일 응용 프로그램을 컴파일하는 방법을 결정하는 경우 훨씬 더 중요한 문제가됩니다 (Android에서는 정적 대 동적을 선택할 수 있으며 iOS에서는 그렇지 않습니다-항상 정적입니다).
C ++ 프로그램은 두 단계로 구축됩니다
정적 라이브러리 (.lib)는 .obj 파일의 번들 일 뿐이므로 완전한 프로그램이 아닙니다. 프로그램을 구축하는 두 번째 (연결) 단계를 거치지 않았습니다. 반면에 DLL은 exe와 유사하므로 완전한 프로그램입니다.
정적 라이브러리를 빌드하는 경우 아직 링크되지 않았으므로 정적 라이브러리 소비자는 사용했던 것과 동일한 컴파일러를 사용해야합니다 (g ++를 사용하는 경우 g ++를 사용해야 함).
대신 dll을 빌드하고 올바르게 빌드 한 경우 사용중인 컴파일러에 관계없이 모든 소비자가 사용할 수있는 완전한 프로그램을 빌드 한 것입니다. 그러나 크로스 컴파일러 호환성이 필요한 경우 dll에서 내보내는 경우 몇 가지 제한 사항이 있습니다.
consumers of your static library will have to use the same compiler that you used
정적 라이브러리와 같은 C ++ 라이브러리를 사용하는 경우 #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]>
시간이 지남에 따른 변경, 버전 관리, 안정성, 호환성 등을 신중하게 고려해야합니다.
공유 코드를 사용하는 두 개의 앱이있는 경우 서로 호환되어야 할 경우를 대비하여 해당 앱을 강제로 변경 하시겠습니까? 그런 다음 dll을 사용하십시오. 모든 exe는 동일한 코드를 사용합니다.
또는 서로를 분리하여 하나를 변경하고 다른 하나를 끊지 않았다고 확신 할 수 있습니다. 그런 다음 정적 lib를 사용하십시오.
DLL 지옥은 아마도 정적 라이브러리를 사용해야했지만 dll을 대신 사용했지만 모든 exe가 호환되지는 않습니다.
정적 라이브러리는 최종 실행 파일에 연결되어야합니다. 실행 파일의 일부가되어 어디를 가든지 따라갑니다. 동적 라이브러리는 실행 파일이 실행될 때마다로드되며 DLL 파일로 실행 파일과 별도로 유지됩니다.
실행 파일을 다시 연결하지 않고도 라이브러리에서 제공하는 기능을 변경할 수있을 때 DLL을 사용합니다 (실행 파일을 바꾸지 않고 DLL 파일 만 바꾸십시오).
동적 라이브러리를 사용할 이유가 없을 때마다 정적 라이브러리를 사용합니다.
" 공유 라이브러리를 작성하는 방법 "에 대한 Ulrich Drepper의 논문 은 또한 공유 라이브러리 를 최대한 활용하는 방법 또는 "Dynamic Shared Objects"(DSO)라고하는 것을 자세히 설명하는 유용한 자료입니다. ELF 바이너리 형식 의 공유 라이브러리에 더 중점을 두지 만 일부 토론은 Windows DLL에도 적합합니다.
우리는 프로젝트에서 많은 DLL (> 100)을 사용합니다. 이 DLL은 서로 의존하므로 동적 링크 설정을 선택했습니다. 그러나 다음과 같은 단점이 있습니다.
아마도 더 나은 설정은 모든 것을 정적 라이브러리 로 만드는 것이므로 실행 파일이 하나뿐입니다. 코드 중복이 발생하지 않는 경우에만 작동합니다. 테스트 가이 가정을 지원하는 것 같지만 공식 MSDN 인용문을 찾을 수 없습니다. 예를 들어 다음과 같이 1 exe를 만드십시오.
shared_lib2의 코드와 변수는 최종 병합 실행 파일에 한 번만 존재해야합니다. 누구든지이 질문을지지 할 수 있습니까?
정적 라이브러리는 해당 코드가 실행 파일로 컴파일 된 애플리케이션에 링크 될 때 라이브러리의 오브젝트 코드가 포함 된 아카이브입니다. 공유 라이브러리는 실행 파일로 컴파일되지 않는다는 점에서 다릅니다. 대신 동적 링커는 필요한 라이브러리를 찾기 위해 일부 디렉토리를 검색 한 다음이를 메모리에로드합니다. 하나 이상의 실행 파일이 동시에 동일한 공유 라이브러리를 사용할 수 있으므로 메모리 사용 및 실행 파일 크기가 줄어 듭니다. 그러나 실행 파일과 함께 배포 할 파일이 더 있습니다. 링커가 찾을 수있는 어딘가에 사용 시스템에 라이브러리가 설치되어 있는지 확인해야합니다. 정적 링크는이 문제를 제거하지만 더 큰 실행 파일을 생성합니다.
나는 당신이 큰 코드베이스를 가지고 있다면 모두 더 낮은 수준의 라이브러리 (예 : Utils 또는 Gui 프레임 워크) 위에 구축되어 더 관리하기 쉬운 라이브러리로 분할 한 다음 정적 라이브러리로 만들고 싶다는 일반적인 규칙을 제시합니다. 동적 라이브러리는 실제로 아무것도 사지 않으며 놀라움이 적습니다. 예를 들어 싱글 톤 인스턴스는 하나뿐입니다.
나머지 코드베이스와 완전히 별 개인 라이브러리 (예 : 타사 라이브러리)가 있으면 dll로 만드는 것을 고려하십시오. 라이브러리가 LGPL 인 경우 라이센스 조건으로 인해 dll을 사용해야 할 수도 있습니다.