g ++ không xác định tham chiếu đến typeinfo


208

Tôi vừa chạy qua lỗi sau (và tìm thấy giải pháp trực tuyến, nhưng nó không có trong Stack Overflow):

(.gnu.linkonce. [thứ]

Tại sao người ta có thể nhận được một trong những lỗi liên kết "không xác định tham chiếu đến typeinfo" này?

(Điểm thưởng nếu bạn có thể giải thích những gì đang diễn ra sau hậu trường.)


31
Tôi biết đó là một bài viết cũ, nhưng tôi đã gặp vấn đề tương tự ngày hôm nay và giải pháp đơn giản là xác định hàm ảo của tôi là ảo abc () {} trong lớp cơ sở, thay vì ảo abc (); mà đã đưa ra lỗi.
Nav

15
tốt hơn nữa là virtual void abc() =0;(nếu phiên bản cơ sở không bao giờ được gọi)
dhardy

3
@Nav: Nếu bạn định nghĩa abc()như vậy, bạn có thể dễ dàng quên xác định lại abc()trong lớp dẫn xuất và nghĩ rằng mọi thứ đều ổn, vì bạn vẫn có thể gọi hàm mà không gặp vấn đề gì. Một cách thực hành tốt để thực hiện các chức năng ảo thuần túy được tìm thấy trong bài viết này và điều này là để làm cho chức năng in "Hàm ảo thuần túy được gọi" và sau đó làm hỏng chương trình.
HelloGoodbye

1
tôi đã có cùng một lỗi. Tôi đã thấy rằng việc thay đổi thứ tự các tham chiếu thành "lib" có thể giúp ích. tôi vừa chuyển vấn đề của lib từ việc
năn nỉ

2
GAH. Đây ít nhất là lần thứ hai tôi điều hướng chính xác đến trang này, để đọc bình luận của @dhardy và tự nói với bản thân mình 'Doh'. Chỉ cần dành 45 phút để cố gắng theo dõi một số hành vi điên rồ và tất cả những gì tôi cần là = 0;.
lùn

Câu trả lời:


222

Một lý do có thể là vì bạn đang khai báo một hàm ảo mà không xác định nó.

Khi bạn khai báo mà không xác định nó trong cùng một đơn vị biên dịch, bạn chỉ ra rằng nó được xác định ở một nơi khác - điều này có nghĩa là pha liên kết sẽ cố gắng tìm nó trong một trong các đơn vị biên dịch (hoặc thư viện) khác.

Một ví dụ về định nghĩa hàm ảo là:

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

Trong trường hợp này, bạn đang đính kèm một định nghĩa vào khai báo, điều đó có nghĩa là trình liên kết không cần phải giải quyết nó sau này.

Dòng

virtual void fn();

tuyên bố fn()mà không xác định nó và sẽ gây ra thông báo lỗi mà bạn đã hỏi về.

Nó rất giống với mã:

extern int i;
int *pi = &i;

trong đó tuyên bố rằng số nguyên iđược khai báo trong một đơn vị biên dịch khác phải được giải quyết tại thời điểm liên kết (nếu không thì pikhông thể được đặt thành địa chỉ của nó).


28
Đó là không chính xác để nói rằng đó virtual void fn() = 0là một định nghĩa. Nó không phải là một định nghĩa, mà chỉ là một tuyên bố . Lý do duy nhất mà trình liên kết không cố gắng giải quyết là mục nhập VMT tương ứng sẽ không đề cập đến một thân hàm (rất có thể sẽ chứa con trỏ null). Tuy nhiên, không ai cấm bạn gọi chức năng ảo thuần túy này theo cách không ảo, tức là bằng cách sử dụng tên đủ điều kiện. Trong trường hợp này các mối liên kết sẽ tìm kiếm cơ thể, và bạn sẽ phải xác định các chức năng. Và vâng, bạn có thể định nghĩa một cơ thể cho một hàm ảo thuần túy.
AnT

1
Và đôi khi người ta thậm chí phải khai báo một cơ thể cho một chức năng ảo thuần túy.
đánh dấu

3
Trình biên dịch (g ++) sẽ cho bạn biết ký hiệu bị thiếu là gì. Lưu ý: Trong trường hợp liên kết thư viện động, bạn có thể nhận được một tên sai. Sử dụng bộ lọc c ++ <mangledNameVariable> để có được nó ở dạng dễ đọc. Lỗi typeinfo với tên lớp là trong trường hợp của tôi do thiếu triển khai hàm hủy ảo trong một số lớp cơ sở.
chmike

1
Câu hỏi đặc biệt đề cập rằng đó là typeinfo bị thiếu, phải làm với rtti. Xem bình luận từ Damon trong stackoverflow.com/questions/11904519/ từ
wilsonmichaelpatrick

1
@gbmhunter, đủ công bằng. Thực hiện thay đổi.
paxdiablo

149

Điều này cũng có thể xảy ra khi bạn trộn -fno-rtti-frttimã. Sau đó, bạn cần đảm bảo rằng bất kỳ lớp nào, type_infođược truy cập trong -frttimã, có phương thức khóa của chúng được biên dịch với -frtti. Truy cập như vậy có thể xảy ra khi bạn tạo một đối tượng của lớp, sử dụng, dynamic_castv.v.

[ nguồn ]


20
CẢM ƠN BẠN RẤT NHIỀU. Điều đó đã khắc phục vấn đề của tôi sau 5 giờ tìm kiếm.
steipete

1
liên kết nguồn đã chết, nó chắc chắn giống như permalink.gmane.org/gmane.comp.gcc.help/32475
toán học

1
Cảm ơn đã chỉ ra điều này. Trang gốc vẫn có sẵn ở đây: web.archive.org/web/20100503172629/http://www.pubbs.net/201004/iêu
Sergiy Belozorov

3
StackOverflow.com để giải cứu một lần nữa! Tôi ước tôi có thể nâng cao hơn một lần. Sau khi đập đầu vào bàn phím trong một giờ, câu trả lời của bạn là điều tôi cần.
spartygw

1
n + 1 mạng được cứu và vẫn còn tiếp tục :)
Gabriel

53

Điều này xảy ra khi các hàm ảo được khai báo (không thuần túy) bị thiếu. Trong định nghĩa lớp học của bạn, một cái gì đó như:

virtual void foo();

Nên được xác định (nội tuyến hoặc trong tệp nguồn được liên kết):

virtual void foo() {}

Hoặc khai báo ảo thuần túy:

virtual void foo() = 0;

27

Trích dẫn từ hướng dẫn gcc :

Đối với các lớp đa hình (các lớp có hàm ảo), đối tượng type_info được viết ra cùng với vtable [...] Đối với tất cả các loại khác, chúng tôi viết ra đối tượng type_info khi nó được sử dụng: khi áp dụng `typeid 'cho một biểu thức, ném một đối tượng hoặc tham chiếu đến một loại trong mệnh đề bắt hoặc đặc tả ngoại lệ.

Và sớm hơn một chút trên cùng một trang:

Nếu lớp khai báo bất kỳ hàm ảo phi tuyến tính, không thuần túy nào, thì hàm đầu tiên được chọn làm phương thức khóa chính của Cameron cho lớp và vtable chỉ được phát ra trong đơn vị dịch thuật trong đó phương thức khóa được xác định.

Vì vậy, lỗi này xảy ra khi "phương thức chính" bị thiếu định nghĩa, như các câu trả lời khác đã được đề cập.


2
Trong trường hợp của tôi, tôi có một lớp cơ sở đã khai báo nhưng không định nghĩa các phương thức ảo không phải là thuần ảo. Khi tôi biến chúng thành ảo hoàn toàn, đó là điều tôi muốn nói, các lỗi liên kết đã biến mất.
Tatiana Racheva

@TatianaRacheva Cảm ơn! Báo cáo lỗi từ trình liên kết ít hữu ích hơn và đối với giao diện lớn, rất dễ bỏ lỡ việc thiếu '= 0;' cho thuần ảo!
chuyến đi

20

Nếu bạn đang liên kết một .so với một cái khác, nhưng một khả năng nữa là biên dịch với "-fvisibility = hidden" trong gcc hoặc g ++. Nếu cả hai tệp .so được xây dựng với "-fvisibility = hidden" và phương thức khóa không giống với .so như một cách triển khai khác của hàm ảo, thì cái sau sẽ không thấy vtable hoặc typeinfo của cái trước. Đối với trình liên kết, nó trông giống như một hàm ảo chưa được thực hiện (như trong câu trả lời của paxdiablo và cdleary).

Trong trường hợp này, bạn phải tạo một ngoại lệ cho khả năng hiển thị của lớp cơ sở với

__attribute__ ((visibility("default")))

trong khai báo lớp. Ví dụ,

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

Tất nhiên, một giải pháp khác là không sử dụng "-fvisibility = hidden." Điều đó làm phức tạp mọi thứ cho trình biên dịch và trình liên kết, có thể gây bất lợi cho hiệu suất mã.


1
Bạn không cần xuất (bỏ ẩn) lớp cơ sở nếu nó là trừu tượng hoặc không được sử dụng, chỉ là các hàm không ảo, thường chỉ là hàm tạo. Mặt khác, các lớp dẫn xuất phải được xuất, nếu chúng được sử dụng.
Chris Huang-Leaver

cảm thấy như một hack, nhưng nó đã giải quyết các triệu chứng về phía tôi. Cảm ơn !
malat

15

Các câu trả lời trước là đúng, nhưng lỗi này cũng có thể được gây ra bằng cách cố gắng sử dụng typeid trên một đối tượng của một lớp không có chức năng ảo. C ++ RTTI yêu cầu một vtable, vì vậy các lớp mà bạn muốn thực hiện nhận dạng kiểu yêu cầu ít nhất một hàm ảo.

Nếu bạn muốn loại thông tin hoạt động trên một lớp mà bạn không thực sự muốn bất kỳ chức năng ảo nào, hãy tạo hàm hủy.


2
Upmodded bởi vì tôi nghĩ rằng nhiều khả năng đây là nguyên nhân của thông báo lỗi cụ thể đó (trái ngược với trường hợp chung hơn của các phương thức không xác định ...)
Alastair

4
Một điều tôi đã phải làm quen với SO là không đề cập đến câu trả lời "ở trên" vì thứ tự có thể thay đổi dựa trên phiếu bầu. Bây giờ tôi không đề cập đến bất kỳ câu trả lời nào khác vì chúng cũng có thể bị xóa. Tôi tin rằng câu trả lời nên độc lập. Tôi vẫn đề cập đến tên người dùng để ghi công tuy nhiên.
paxdiablo

Bạn có thể sử dụng typeid mà không cần vtable; xem câu trả lời của tôi cho các trích dẫn từ hướng dẫn gcc.
CesarB

11

Tôi mới dành vài giờ cho lỗi này và trong khi các câu trả lời khác ở đây giúp tôi hiểu chuyện gì đang xảy ra, họ đã không khắc phục vấn đề cụ thể của tôi.

Tôi đang làm việc trên một dự án biên dịch bằng cả hai clang++g++. Tôi đã không có vấn đề liên kết sử dụng clang++, nhưng đã nhận được undefined reference to 'typeinfo forlỗi với g++.

Điểm quan trọng: Liên kết thứ tự MATTERS với g++. Nếu bạn liệt kê các thư viện bạn muốn liên kết theo thứ tự không chính xác, bạn có thể gặp typeinfolỗi.

Xem câu hỏi SO này để biết thêm chi tiết về thứ tự liên kết với gcc/ g++.


Cảm ơn bạn!!! Tôi đã dành hơn một ngày để tìm hiểu lý do tại sao tôi gặp phải lỗi này và không có gì hoạt động cho đến khi tôi thấy câu trả lời này và câu trả lời của bạn. Cám ơn rất nhiều!!
Irene

10

Các giải pháp khả thi cho mã liên quan đến các thư viện RTTI và không phải RTTI:

a) Biên dịch lại mọi thứ với -frtti hoặc -fno-rtti
b) Nếu a) không thể cho bạn, hãy thử các cách sau:

Giả sử libfoo được xây dựng mà không có RTTI. Mã của bạn sử dụng libfoo và biên dịch với RTTI. Nếu bạn sử dụng một lớp (Foo) trong libfoo có ảo, bạn có thể gặp phải lỗi thời gian liên kết có nội dung: thiếu typeinfo cho lớp Foo.

Xác định một lớp khác (ví dụ FooAd CHƯƠNG) không có ảo và sẽ chuyển tiếp các cuộc gọi đến Foo mà bạn sử dụng.

Biên dịch FooAd CHƯƠNG trong một thư viện tĩnh nhỏ không sử dụng RTTI và chỉ phụ thuộc vào các biểu tượng libfoo. Cung cấp một tiêu đề cho nó và sử dụng nó trong mã của bạn (sử dụng RTTI). Vì FooAd CHƯƠNG không có chức năng ảo nên nó sẽ không có bất kỳ loại thông tin nào và bạn sẽ có thể liên kết nhị phân của mình. Nếu bạn sử dụng nhiều lớp khác nhau từ libfoo, giải pháp này có thể không thuận tiện, nhưng đó là một sự khởi đầu.


Đây là nó cho tôi, liên kết đến một thư viện với các cài đặt RTTI khác nhau.
đầm lầy

6

Tương tự như cuộc thảo luận RTTI, NO-RTTI ở trên, vấn đề này cũng có thể xảy ra nếu bạn sử dụng Dynamic_cast và không bao gồm mã đối tượng có chứa triển khai lớp.

Tôi gặp phải vấn đề này khi xây dựng Cygwin và sau đó chuyển mã sang Linux. Các tệp tạo, cấu trúc thư mục và thậm chí các phiên bản gcc (4.8.2) giống hệt nhau trong cả hai trường hợp, nhưng mã được liên kết và vận hành chính xác trên Cygwin nhưng không thể liên kết trên Linux. Red Hat Cygwin rõ ràng đã thực hiện các sửa đổi trình biên dịch / trình liên kết để tránh yêu cầu liên kết mã đối tượng.

Thông báo lỗi liên kết Linux đã đưa tôi đến dòng Dynamic_cast, nhưng các thông báo trước đó trong diễn đàn này đã cho tôi tìm kiếm các triển khai chức năng bị thiếu thay vì vấn đề thực tế: thiếu mã đối tượng. Cách giải quyết của tôi là thay thế một hàm loại ảo trong lớp cơ sở và lớp dẫn xuất, ví dụ: int int isecialType (), thay vì sử dụng Dynamic_cast. Kỹ thuật này tránh yêu cầu liên kết mã triển khai đối tượng chỉ để động_cast hoạt động chính xác.


5

Trong lớp cơ sở (một lớp cơ sở trừu tượng), bạn khai báo một hàm hủy ảo và vì bạn không thể khai báo một hàm hủy là một hàm ảo thuần túy, hoặc bạn phải định nghĩa nó ngay tại đây trong lớp trừu tượng, chỉ là một định nghĩa giả như ảo ~ cơ sở ( ) {} sẽ làm hoặc trong bất kỳ lớp dẫn xuất nào.

Nếu bạn không làm điều này, bạn sẽ kết thúc bằng một "biểu tượng không xác định" tại thời điểm liên kết. Vì VMT có một mục nhập cho tất cả các hàm ảo thuần túy với NULL phù hợp khi nó cập nhật bảng tùy thuộc vào việc triển khai trong lớp dẫn xuất. Nhưng đối với các hàm không thuần túy nhưng ảo, nó cần định nghĩa tại thời điểm liên kết để có thể cập nhật bảng VMT.

Sử dụng bộ lọc c ++ để khử biểu tượng. Giống như bộ lọc $ c ++ _ZTIN10storageapi8BasehostE sẽ xuất ra một cái gì đó như "typeinfo cho repositoryageapi :: Basehost".


3

Tôi đã nhận được rất nhiều những lỗi này ngay bây giờ. Điều xảy ra là tôi chia một lớp chỉ có tệp tiêu đề thành tệp tiêu đề và tệp cpp. Tuy nhiên, tôi đã không cập nhật hệ thống xây dựng của mình, vì vậy tệp cpp không được biên dịch. Trong số chỉ đơn giản là có các tham chiếu không xác định đến các hàm được khai báo trong tiêu đề nhưng không được triển khai, tôi đã gặp rất nhiều lỗi typeinfo này.

Giải pháp là chạy lại hệ thống xây dựng để biên dịch và liên kết tệp cpp mới.


3

trong trường hợp của tôi, tôi đã sử dụng thư viện của bên thứ ba với các tệp tiêu đề và tệp đó. tôi đã phân lớp một lớp và lỗi liên kết như thế này xảy ra khi tôi cố gắng khởi tạo lớp con của mình.

như được đề cập bởi @sergiy, biết rằng đó có thể là vấn đề của 'rtti', tôi đã quản lý để khắc phục nó bằng cách đặt triển khai hàm tạo vào tệp .cpp riêng biệt và áp dụng các cờ biên dịch '-fno-rtti' cho tệp . nó hoạt động tốt

vì tôi vẫn chưa rõ về nội bộ của lỗi liên kết này, tôi không chắc liệu giải pháp của tôi có chung chung hay không. tuy nhiên, tôi nghĩ rằng nó đáng để thử trước khi thử cách chuyển đổi như được đề cập bởi @francois. và tất nhiên, nếu tất cả các mã nguồn đều có sẵn (không phải trong trường hợp của tôi), tốt hơn nên biên dịch lại với '-frtti' nếu có thể.

một điều nữa, nếu bạn chọn thử giải pháp của tôi, hãy thử tạo tệp riêng biệt đơn giản nhất có thể và không sử dụng một số tính năng ưa thích của C ++. đặc biệt chú ý đến những thứ liên quan đến boost, vì phần lớn nó phụ thuộc vào rtti.


2

Tôi đã gặp lỗi tương tự khi giao diện của tôi (với tất cả các chức năng ảo thuần túy) cần thêm một chức năng và tôi đã quên "null" nó.

Tôi đã có

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(); };

Lần cuối vaClose không phải là ảo nên đã biên dịch không biết nơi nào để thực hiện cho nó và do đó bị lẫn lộn. tin nhắn của tôi là:

... TCPClient.o :(. Rodata + 0x38): tham chiếu không xác định đến `typeinfo cho ICommProvider '

Thay đổi đơn giản từ

virtual int vaClose();

đến

virtual int vaClose() = 0;

đã khắc phục sự cố. hy vọng nó giúp


1

Tôi gặp một tình huống rất hiếm, nhưng điều này có thể giúp những người bạn khác trong tình huống tương tự. Tôi phải làm việc trên một hệ thống cũ hơn với gcc 4.4.7. Tôi phải biên dịch mã với hỗ trợ c ++ 11 trở lên, vì vậy tôi xây dựng phiên bản mới nhất của gcc 5.3.0. Khi xây dựng mã của tôi và liên kết với các phụ thuộc nếu phụ thuộc được xây dựng với trình biên dịch cũ hơn, thì tôi đã gặp lỗi 'tham chiếu không xác định' mặc dù tôi đã xác định rõ đường dẫn liên kết với -L / path / to / lib -llibname. Một số gói như boost và dự án xây dựng với cmake thường có xu hướng sử dụng trình biên dịch cũ hơn và chúng thường gây ra các vấn đề như vậy. Bạn phải đi một chặng đường dài để đảm bảo họ sử dụng trình biên dịch mới hơn.


1

Trong trường hợp của tôi, đây hoàn toàn là vấn đề phụ thuộc thư viện ngay cả khi tôi có lệnh gọi Dynamic_cast. Sau khi thêm đủ phụ thuộc vào makefile, vấn đề này đã biến mất.


0

Kiểm tra xem các phụ thuộc của bạn đã được biên dịch mà không có -f-nortti.

Đối với một số dự án, bạn phải đặt nó rõ ràng, như trong RocksDB:

USE_RTTI=1 make shared_lib -j4

0

Trong trường hợp của tôi, đó là một hàm ảo trong một lớp giao diện không được định nghĩa là ảo thuần túy.

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

Tôi quên một = 0chút.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.