typeinfo에 대한 g ++ 정의되지 않은 참조


208

방금 다음 오류가 발생했습니다 (온라인에서 솔루션을 찾았지만 스택 오버플로에는 표시되지 않음).

(.gnu.linkonce. [stuff]) : [method] [object file] :(. gnu.linkonce. [stuff])에 대한 정의되지 않은 참조 : [classname]의 typeinfo에 대한 정의되지 않은 참조

왜 "typeinfo에 대한 정의되지 않은 참조"링커 오류 중 하나가 발생합니까?

(뒤에서 일어나는 일을 설명 할 수 있다면 보너스 포인트.)


31
나는 그것이 오래된 게시물이라는 것을 알고 있지만 오늘도 같은 문제가 있었으며 해결책은 단순히 가상 abc () 대신 기본 클래스에서 virtual abc () {}로 가상 함수를 정의하는 것이 었습니다. 오류가 발생했습니다.
Nav

15
더 나은 아직 virtual void abc() =0;(기본 버전이 호출 되지 않은 경우)
dhardy

3
@ Nav : 그렇게 정의 하면 파생 클래스에서 abc()재정의 abc()하는 것을 잊어 버릴 수 있으며 모든 것이 괜찮다고 생각할 수 있습니다. 문제없이 함수를 호출 할 수 있기 때문입니다. 순수한 가상 함수를 구현하는 좋은 방법은 이 기사 에서 찾을 수 있으며, 이는 함수가 "Pure virtual function called"를 인쇄 한 다음 프로그램을 중단시키는 것입니다.
HelloGoodbye

1
나는 같은 오류가 발생했습니다. "lib"에 대한 참조 순서를 변경하면 도움이 될 수 있습니다. 난 그냥리스트의 말미에 문제 LIB 년대 beggining에서 이동이 문제 해결
javapowered

2
GAH. @dhardy의 코멘트를 읽고 나 자신에게 'Doh'라고 말하기 위해 지금이 페이지로 정확하게 이동 한 것은 이제 두 번째입니다. 미친 행동을 추적하려고 45 분을 보냈는데 필요한 것은 전부였습니다 = 0;.
dwanderson

답변:


222

가능한 한 가지 이유는 가상 함수를 정의하지 않고 선언했기 때문입니다.

동일한 컴파일 단위로 정의하지 않고 선언하면 다른 위치에 정의되어 있음을 나타냅니다. 즉, 링커 단계는 다른 컴파일 단위 (또는 라이브러리) 중 하나에서 해당 링크를 찾으려고합니다.

가상 기능을 정의하는 예는 다음과 같습니다.

virtual void fn() { /* insert code here */ }

이 경우 선언에 정의를 첨부하므로 링커에서 나중에이를 해결할 필요가 없습니다.

라인

virtual void fn();

fn()정의하지 않고 선언 하면 요청한 오류 메시지가 나타납니다.

코드와 매우 유사합니다.

extern int i;
int *pi = &i;

이것은 i링크 타임에 해결 해야하는 다른 컴파일 단위에서 정수 가 선언 되었음을 나타냅니다 (그렇지 않으면 pi주소로 설정할 수 없음).


28
그것이 virtual void fn() = 0정의 라고 말하는 것은 잘못 입니다. 그것은 정의가 아니라 단순한 선언 이다. 링커가이를 해결하려고하지 않는 유일한 이유는 해당 VMT 항목이 함수 본문을 참조하지 않기 때문입니다 (널 포인터를 포함 할 가능성이 가장 높음). 그러나이 순수한 가상 함수를 가상이 아닌 방식으로, 즉 완전한 이름을 사용하여 호출하는 것을 아무도 금지하지 않습니다. 이 경우 링커 에서 본문 찾고 함수를 정의해야합니다. 그렇습니다 . 순수한 가상 기능을위한 바디를 정의 할 수 있습니다 .
AnT

1
때로는 순수한 가상 함수를 위해 본문을 선언해야하는 경우도 있습니다.
마크

3
컴파일러 (g ++)는 누락 된 기호가 무엇인지 알려줍니다. 참고 : 동적 라이브러리 연결의 경우 맹 글링 된 이름이 표시 될 수 있습니다. c ++ filt <mangledNameVariable>을 사용하여 읽을 수있는 형식으로 만드십시오. 클래스 이름의 typeinfo 오류는 일부 기본 클래스에서 누락 된 가상 소멸자 구현으로 인해 제 경우에 발생했습니다.
chmike

1
질문은 특히 누락 된 typeinfo이며 rtti와 관련이 있다고 언급합니다. 에서 데이먼의 의견을 참조하십시오 stackoverflow.com/questions/11904519/...
wilsonmichaelpatrick

1
@gbmhunter, 충분히 공평합니다. 변경했습니다.
paxdiablo

149

혼합 -fno-rtti하고 -frtti코딩 할 때도 발생할 수 있습니다 . 그런 다음 코드 type_info에서 액세스 하는 모든 클래스에 -frtti키 메소드가로 컴파일되어 있는지 확인해야합니다 -frtti. 이러한 액세스는 클래스의 객체를 만들거나 사용할 때 발생할 수 있습니다 dynamic_cast.

[ 출처 ]


20
정말 고맙습니다. 5 시간 검색 후 내 문제가 해결되었습니다.
steipete

1
소스 링크가 죽었을 때 반드시 permalink.gmane.org/gmane.comp.gcc.help/32475
math

1
이것을 지적 해 주셔서 감사합니다. 원본 페이지는 여전히 여기에 있습니다 : web.archive.org/web/20100503172629/http://www.pubbs.net/201004/...
세르지 Belozorov

3
다시 구조에 StackOverflow.com! 한 번 이상 공표 할 수 있기를 바랍니다. 한 시간 동안 키보드로 머리를 두드리면 대답이 필요했습니다.
spartygw

1
n + 1 생명을 구하고 여전히 계산 :)
Gabriel

53

선언 된 (순수하지 않은) 가상 함수에 본문이없는 경우 발생합니다. 클래스 정의에서 다음과 같습니다.

virtual void foo();

인라인 또는 링크 된 소스 파일로 정의해야합니다.

virtual void foo() {}

또는 순수한 가상 선언 :

virtual void foo() = 0;

27

gcc 매뉴얼 에서 인용 :

다형성 클래스 (가상 함수가있는 클래스)의 경우, type_info 객체는 vtable과 함께 작성됩니다. 객체를 던지거나 catch 절 또는 예외 사양의 유형을 참조합니다.

같은 페이지에서 조금 더 일찍

클래스가 인라인이 아닌 순수한 가상 함수를 선언하면 첫 번째 함수가 클래스의 "키 메소드"로 선택되고 vtable은 키 메소드가 정의 된 변환 단위에서만 생성됩니다.

따라서이 오류는 다른 답변에서 이미 언급했듯이 "키 방법"에 정의가없는 경우 발생합니다.


2
필자의 경우 순수 가상이 아닌 가상 메서드를 선언했지만 정의하지 않은 기본 클래스가 있습니다. 일단 내가 가상의 순수한 가상을 만들면 링커 오류가 사라졌습니다.
Tatiana Racheva

@TatianaRacheva 감사합니다! 링커의 오류보고는 도움이되지 않으며 대규모 인터페이스의 경우 '= 0; 순수한 가상!
rholmes

20

하나의 .so를 다른 .so에 연결하는 경우 gcc 또는 g ++에서 "-fvisibility = hidden"으로 컴파일 할 수 있습니다. 두 개의 .so 파일이 모두 "-fvisibility = hidden"으로 빌드되고 키 메소드가 다른 가상 함수 구현과 동일하지 않은 경우, 후자는 전자의 vtable 또는 typeinfo를 볼 수 없습니다. 링커에게 이것은 구현되지 않은 가상 함수처럼 보입니다 (paxdiablo 및 cdleary의 답변에서와 같이).

이 경우 기본 클래스의 가시성을 예외로 설정해야합니다.

__attribute__ ((visibility("default")))

클래스 선언에서. 예를 들어

class __attribute__ ((visibility("default"))) boom{
    virtual void stick();
}

물론 다른 해결책은 "-fvisibility = hidden"을 사용하지 않는 것입니다. 컴파일러와 링커의 경우 코드 성능이 저하 될 수 있습니다.


1
기본 클래스가 추상적이거나 사용되지 않는 경우, 비가 상 함수, 일반적으로 생성자 인 경우 기본 클래스를 내보내거나 숨길 필요가 없습니다. 반면 파생 클래스는 사용되는 경우 내 보내야합니다.
Chris Huang-Leaver

해킹처럼 느껴지지만 내 측면의 증상을 해결했습니다. 감사 !
malat

15

이전 답변은 정확하지만이 오류는 가상 함수 가 없는 클래스의 객체에서 typeid를 사용하여 발생할 수도 있습니다 . C ++ RTTI에는 vtable이 필요하므로 유형 식별을 수행하려는 클래스에는 하나 이상의 가상 함수가 필요합니다.

실제로 가상 함수를 원하지 않는 클래스에서 유형 정보를 작동 시키려면 소멸자를 가상으로 만듭니다.


2
이것이 정의되지 않은 메소드의 일반적인 경우와 달리 특정 오류 메시지의 원인 일 가능성이 높다고 생각하여 업데이트되었습니다.
Alastair

4
투표에 따라 순서가 변경 될 수 있으므로 SO와 익숙해 져야하는 한 가지는 "위의"답변을 언급하지 않는 것입니다. 나는 일반적으로 다른 답변도 삭제할 수 있기 때문에 지금은 언급하지 않습니다. 제 생각은 답변은 독립형이어야한다는 것입니다. 그러나 여전히 기여에 대한 사용자 이름을 참조합니다.
paxdiablo

vtable없이 typeid를 사용할 수 있습니다. gcc 매뉴얼의 인용문에 대한 내 대답을 참조하십시오.
CesarB

11

나는이 오류에 대해 몇 시간을 보냈고 여기에있는 다른 대답은 무슨 일이 일어나고 있는지 이해하는 데 도움이되었지만 내 특정 문제는 해결하지 못했습니다.

나는 컴파일이 모두 사용하는 프로젝트를 진행하고 clang++g++. 을 (를) 사용하는 연결 문제가 clang++없었지만에 undefined reference to 'typeinfo for오류가 발생했습니다 g++.

중요한 점 : 주문 MATTERS와 (과) 연결 g++. 연결하려는 라이브러리를 잘못된 순서로 나열하면 typeinfo오류가 발생할 수 있습니다 .

/ 와 순서를 연결하는 방법에 대한 자세한 내용 은 이 SO 질문 을 참조하십시오 .gccg++


감사합니다!!! 나는 하루 동안이 오류가 발생하는 이유를 찾으려고 노력 했으며이 회신과 연결된 답변을 볼 때까지 아무런 효과가 없었습니다. 정말 고마워!!
아이린

10

RTTI 및 비 RTTI 라이브러리를 처리하는 코드에 대한 가능한 솔루션 :

a) -frtti 또는 -fno-rtti를 사용하여 모든 것을 다시 컴파일하십시오.
b) a)를 사용할 수없는 경우 다음을 시도하십시오.

libfoo가 RTTI없이 빌드되었다고 가정하십시오. 코드는 libfoo를 사용하고 RTTI로 컴파일합니다. 가상이있는 libfoo에서 클래스 (Foo)를 사용하는 경우 링크 시간 오류 (Foo 클래스에 대한 typeinfo 누락)가 발생할 수 있습니다.

가상이없고 사용중인 Foo로 통화를 전달하는 다른 클래스 (예 : FooAdapter)를 정의하십시오.

RTTI를 사용하지 않고 libfoo 기호에만 의존하는 작은 정적 라이브러리에서 FooAdapter를 컴파일하십시오. 헤더를 제공하고 대신 RTTI를 사용하는 코드에서 사용하십시오. FooAdapter에는 가상 기능이 없으므로 typeinfo가 없으므로 바이너리를 연결할 수 있습니다. libfoo와 다른 클래스를 많이 사용하는 경우이 솔루션은 편리하지는 않지만 시작입니다.


이것은 RTTI 설정이 다른 라이브러리에 연결하는 것이 었습니다.
marsh

6

위의 RTTI, NO-RTTI 토론과 마찬가지로이 문제는 dynamic_cast를 사용하고 클래스 구현을 포함하는 객체 코드를 포함하지 않는 경우에도 발생할 수 있습니다.

Cygwin 에서이 문제를 겪고 코드를 Linux로 이식했습니다. make 파일, 디렉토리 구조 및 gcc 버전 (4.8.2)은 두 경우 모두 동일했지만 코드는 Cygwin에서 올바르게 링크되고 작동했지만 Linux에서는 링크되지 않았습니다. Red Hat Cygwin은 분명히 객체 코드 연결 요구 사항을 피하기 위해 컴파일러 / 링커 수정을 수행했습니다.

Linux 링커 오류 메시지가 제대로 dynamic_cast 행으로 안내되었지만이 포럼의 이전 메시지에서 실제 문제가 아닌 누락 된 함수 구현 (오브젝트 코드 누락)을 찾고있었습니다. 내 해결 방법은 dynamic_cast를 사용하는 대신 기본 및 파생 클래스에서 가상 유형 함수 (예 : virtual int isSpecialType ())를 대체하는 것입니다. 이 기술은 dynamic_cast가 제대로 작동하기 위해 객체 구현 코드를 연결하지 않아도됩니다.


5

기본 클래스 (추상 기본 클래스)에서 가상 소멸자를 선언하고 소멸자를 순수한 가상 함수로 선언 할 수 없으므로 가상 클래스에서 가상 ~베이스 (와 같은 더미 정의)를 정의해야합니다 ) {}는 파생 클래스에서 수행합니다.

이 작업을 수행하지 않으면 링크 타임에 "정의되지 않은 심볼"로 표시됩니다. VMT는 파생 클래스의 구현에 따라 테이블을 업데이트 할 때 일치하는 NULL을 가진 모든 순수 가상 함수에 대한 항목을 가지고 있으므로 그러나 순수하지는 않지만 가상 함수의 경우 VMT 테이블을 업데이트 할 수 있도록 링크 타임에 정의가 필요합니다.

c ++ filt를 사용하여 심볼을 엉 키게합니다. $ c ++ filt와 같이 _ZTIN10storageapi8BaseHostE는 "typeinfo for storageapi :: BaseHost"와 같은 것을 출력합니다.


3

지금 막 많은 오류가 발생했습니다. 헤더 파일 전용 클래스를 헤더 파일과 cpp 파일로 분할했습니다. 그러나 빌드 시스템을 업데이트하지 않았으므로 cpp 파일이 컴파일되지 않았습니다. 헤더에 선언되었지만 구현되지 않은 함수에 대한 정의되지 않은 참조가있는 가운데 많은 typeinfo 오류가 발생했습니다.

해결책은 빌드 시스템을 다시 실행하여 새 cpp 파일을 컴파일하고 링크하는 것입니다.


3

필자의 경우 헤더 파일과 파일이있는 타사 라이브러리를 사용했습니다. 하나의 클래스를 서브 클래스 화했으며 서브 클래스를 인스턴스화하려고 할 때 이와 같은 링크 오류가 발생했습니다.

@sergiy가 언급했듯이 'rtti'의 문제 일 수 있다는 것을 알았으므로 생성자 구현을 별도의 .cpp 파일에 넣고 '-fno-rtti'컴파일 플래그를 파일에 적용 하여 해결할 수있었습니다 . 잘 작동한다.

이 링크 오류의 내부에 대해 여전히 명확하지 않기 때문에 내 솔루션이 일반적인지 확실하지 않습니다. 그러나 @francois에서 언급 한대로 어댑터 방식을 시도하기 전에 한 번 가치가 있다고 생각합니다. 물론 모든 소스 코드를 사용할 수 있다면 (필자의 경우 아님) 가능한 경우 '-frtti'로 다시 컴파일하는 것이 좋습니다.

한 가지 더, 내 솔루션을 사용하기로 선택한 경우 가능한 한 별도의 파일을 간단하게 만들고 C ++의 멋진 기능을 사용하지 마십시오. 부스트 관련 사항에 특별한주의를 기울이십시오. 많은 것이 rtti에 달려 있습니다.


2

내 인터페이스 (모든 순수한 가상 함수 포함)에 하나 이상의 기능이 필요할 때 동일한 오류가 발생하여 "널 (null)"을 잊어 버렸습니다.

나는했다

class ICommProvider { public: /** * @brief If connection is established, it sends the message into the server. * @param[in] msg - message to be send * @return 0 if success, error otherwise */ virtual int vaSend(const std::string &msg) = 0; /** * @brief If connection is established, it is waiting will server response back. * @param[out] msg is the message received from server * @return 0 if success, error otherwise */ virtual int vaReceive(std::string &msg) = 0; virtual int vaSendRaw(const char *buff, int bufflen) = 0; virtual int vaReceiveRaw(char *buff, int bufflen) = 0; /** * @bief Closes current connection (if needed) after serving * @return 0 if success, error otherwise */ virtual int vaClose(); };

마지막 vaClose는 가상이 아니기 때문에 컴파일하여 구현할 위치를 알지 못하여 혼란 스러웠습니다. 내 메시지는 :

... TCPClient.o :(. rodata + 0x38) :`typeinfo for ICommProvider '에 대한 정의되지 않은 참조

간단한 변경

virtual int vaClose();

virtual int vaClose() = 0;

문제를 해결했습니다. 그것이 도움이되기를 바랍니다


1

드문 상황이 발생하지만 비슷한 상황에있는 다른 친구에게 도움이 될 수 있습니다. gcc 4.4.7이 설치된 구형 시스템에서 작업해야합니다. c ++ 11 이상을 지원하는 코드를 컴파일해야하므로 최신 버전의 gcc 5.3.0을 빌드하십시오. 의존성이 오래된 컴파일러로 빌드되면 코드를 빌드하고 의존성에 연결할 때 -L / path / to / lib -llibname으로 연결 경로를 명확하게 정의했지만 '정의되지 않은 참조'오류가 발생합니다. cmake를 사용하여 boost 및 프로젝트 빌드와 같은 일부 패키지는 일반적으로 이전 컴파일러를 사용하는 경향이 있으며 일반적으로 이러한 문제가 발생합니다. 최신 컴파일러를 사용하려면 먼 길을 가야합니다.


1

필자의 경우 dynamic_cast 호출이 있어도 순수하게 라이브러리 종속성 문제입니다. makefile에 충분한 종속성을 추가 한 후이 문제는 사라졌습니다.


0

종속성이없이 컴파일되었는지 확인하십시오 -f-nortti.

일부 프로젝트의 경우 RocksDB와 같이 명시 적으로 설정해야합니다.

USE_RTTI=1 make shared_lib -j4

0

필자의 경우 순수한 가상으로 정의되지 않은 인터페이스 클래스의 가상 함수였습니다.

class IInterface
{
public:
  virtual void Foo() = 0;
}

나는 = 0조금 잊었다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.