在 C++ 中定义 class 析构函数时未定义的引用
Undefined reference while defining a class destructor in C++
我正在做一个小项目来提高我在 C++ 中的 OOP 技能 - 该项目只是一个简单的书籍集合,有点像家庭图书馆,书籍可以存储在虚拟书架上,分组成更大的书架具有各自的名称等,每本书还有自己的 ID,是一个正整数。我试图创建一个用户定义的 class 析构函数,我想将析构函数的实现移动到源文件,遵循将超过一行的实现移动到 [= 的规则50=].cpp 文件来自 .hpp 文件以帮助养成产生更好的习惯优化和更快的代码(当然在这个例子中它无济于事,但我只是想找到解决遇到的问题的方法,而不是在以后挣扎)。
但是,在尝试编译我的代码时,出现以下错误:
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles/bookshelf.dir/objects.a(bookshelf.cpp.obj): in function `void std::_Destroy<Book>(Book*)':
c:/mingw/include/c++/9.2.0/bits/stl_construct.h:98: undefined reference to `Book::~Book()'
collect2.exe: error: ld returned 1 exit status
make.exe[2]: *** [CMakeFiles/bookshelf.dir/build.make:101: bookshelf.exe] Error 1
make.exe[1]: *** [CMakeFiles/Makefile2:139: CMakeFiles/bookshelf.dir/all] Error 2
make.exe: *** [Makefile:111: all] Error 2
The terminal process "C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe -Command cmake --build Build" terminated with exit code: 1.
我使用 Visual Studio 代码并使用 MinGW64 编译器编译我的代码,在 Windows 11 上工作,我的项目使用以下 CMakeLists 文件设置:
cmake_minimum_required(VERSION 3.0.0)
project(Book VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# include(GoogleTest)
# enable_testing()
add_executable(main main.cpp)
add_executable(book src/book.cpp include/bookshelf.hpp include/book.hpp include/types.hpp include/helpers.hpp)
add_executable(bookshelf src/bookshelf.cpp include/bookshelf.hpp)
# add_executable(library src/library.cpp include/library.hpp)
add_compile_options(-Werror -Wextra -Wall -Wconversion -Wpedantic -pedantic-errors -unused-variable)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
include_directories(
include
src
)
这是头文件和源文件的代码:
book.cpp
#include <cstdlib>
#include <iostream>
#include <set>
#include "book.hpp"
#include "helpers.hpp"
Book::~Book(){
ids.erase(book_id_);
freed_ids.insert(book_id_);
//@TODO: To be implemented - destroy the object, not only deal with the book's ID!
};
Book::Book(Title title, Author author) : title_(title), author_(author){
if(!freed_ids.empty()){
book_id_ = *(freed_ids.begin());
}
else if(freed_ids.empty() && !ids.empty()){
book_id_ = *(ids.end());
}
else{
book_id_ = 0;
}
ids.insert(book_id_);
};
int main(){
std::cout << "Hello, from book.cpp!" << std::endl;
return EXIT_SUCCESS;
}
book.hpp
#ifndef book
#define book
#include "types.hpp"
#include "helpers.hpp"
/*
Class represeting a book as en entry in a collection of books.
@param author_ Author of the book.
@param title_ Title of the book.
@param book_id_ ID of a book.
*/
class Book{
private:
Author author_;
Title title_;
BookID book_id_;
public:
Book(Title title, Author author);
/*Get the title of the book.
@TODO: To be moved into a new class Library*/
Title get_title() const {return title_;}
/*Get the author of the book.
@TODO: To be moved into a new class Library*/
Author get_author() const {return author_;}
/*Get the ID number of the book.
@TODO: To be moved into a new class Library*/
BookID get_id() const {return book_id_;}
~Book();
};
#endif
只需将析构函数的主体移动到 .hpp 文件即可使代码编译正常,但这并不能解决我的问题. .cpp 文件中定义的构造函数工作得很好,我不明白为什么析构函数不起作用。
在此先感谢您的帮助!
P.S。如果我的代码中有什么地方没有意义而且很愚蠢,请随时告诉我,我将不胜感激。
@user17732522 我已经应用了你在下面的答案中建议我的更改,现在析构函数的问题没有发生,而是我收到错误消息,指出我使用的 std::set 对象有多个定义存储 ID 和名称 - 它们看起来像这样:
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
CMakeFiles/main.dir/objects.a(bookshelf.cpp.obj):C:/Users/Darek/Desktop/projects/myLibrary/include/helpers.hpp:9: multiple definition of `ids';
CMakeFiles/main.dir/objects.a(book.cpp.obj):C:/Users/Darek/Desktop/projects/myLibrary/include/helpers.hpp:9: first defined here
还有两个类似的错误与那个一起出现,除了它们指的是 freed_ids 和 shelf_names std::set 类型的对象。
这里是helpers.hpp文件,不是很复杂:
#ifndef HELPERS_HEADER_GUARD
#define HELPERS_HEADER_GUARD
#include <cstdlib>
#include <set>
#include <utility>
#include "types.hpp"
std::set<BookID> ids;
std::set<BookID> freed_ids;
std::set<ShelfName> shelf_names;
#endif
并且 bookshelf.cpp 文件也出现在错误消息中:
#include <iostream>
#include <vector>
#include <exception>
#include <algorithm>
#include "bookshelf.hpp"
Bookshelf::Bookshelf(ShelfName shelf_name){
if(std::find(shelf_names.begin(), shelf_names.end(), shelf_name) != shelf_names.end()){
throw(std::invalid_argument("A bookshelf with the given name already exists."));
}
else{
shelf_name_ = shelf_name;
// content_ = {};
}
}
您正在将您的程序分成多个:
add_executable(main main.cpp)
add_executable(book src/book.cpp include/bookshelf.hpp include/book.hpp include/types.hpp include/helpers.hpp)
add_executable(bookshelf src/bookshelf.cpp include/bookshelf.hpp)
您只需要一个可执行文件。此外,头文件不属于那里:
add_executable(main main.cpp src/book.cpp src/bookshelf.cpp)
(顺便说一句,为什么 main.cpp
不在 src
中?)
另一方面,include_directories
应该只包含 include
目录:
include_directories(
include
)
出现undefined reference错误的原因是,在你原来的配置下,CMake会创建三个独立的程序。只考虑这个:
add_executable(bookshelf src/bookshelf.cpp include/bookshelf.hpp)
它将通过编译 src/bookshelf.cpp
来构建,而不是 src/book.cpp
。 (.hpp
文件在这里无关紧要,但仍然不属于命令。)
所以bookshelf.cpp
可能包含一些Book
类型的(成员)变量。为了使用这个变量,程序需要知道如何构造和销毁该类型的变量。也就是说,它需要有Book
的构造函数和析构函数的定义。但是两者的定义只在文件book.cpp
中,不包含在this程序编译中
我正在做一个小项目来提高我在 C++ 中的 OOP 技能 - 该项目只是一个简单的书籍集合,有点像家庭图书馆,书籍可以存储在虚拟书架上,分组成更大的书架具有各自的名称等,每本书还有自己的 ID,是一个正整数。我试图创建一个用户定义的 class 析构函数,我想将析构函数的实现移动到源文件,遵循将超过一行的实现移动到 [= 的规则50=].cpp 文件来自 .hpp 文件以帮助养成产生更好的习惯优化和更快的代码(当然在这个例子中它无济于事,但我只是想找到解决遇到的问题的方法,而不是在以后挣扎)。
但是,在尝试编译我的代码时,出现以下错误:
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles/bookshelf.dir/objects.a(bookshelf.cpp.obj): in function `void std::_Destroy<Book>(Book*)':
c:/mingw/include/c++/9.2.0/bits/stl_construct.h:98: undefined reference to `Book::~Book()'
collect2.exe: error: ld returned 1 exit status
make.exe[2]: *** [CMakeFiles/bookshelf.dir/build.make:101: bookshelf.exe] Error 1
make.exe[1]: *** [CMakeFiles/Makefile2:139: CMakeFiles/bookshelf.dir/all] Error 2
make.exe: *** [Makefile:111: all] Error 2
The terminal process "C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe -Command cmake --build Build" terminated with exit code: 1.
我使用 Visual Studio 代码并使用 MinGW64 编译器编译我的代码,在 Windows 11 上工作,我的项目使用以下 CMakeLists 文件设置:
cmake_minimum_required(VERSION 3.0.0)
project(Book VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# include(GoogleTest)
# enable_testing()
add_executable(main main.cpp)
add_executable(book src/book.cpp include/bookshelf.hpp include/book.hpp include/types.hpp include/helpers.hpp)
add_executable(bookshelf src/bookshelf.cpp include/bookshelf.hpp)
# add_executable(library src/library.cpp include/library.hpp)
add_compile_options(-Werror -Wextra -Wall -Wconversion -Wpedantic -pedantic-errors -unused-variable)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
include_directories(
include
src
)
这是头文件和源文件的代码: book.cpp
#include <cstdlib>
#include <iostream>
#include <set>
#include "book.hpp"
#include "helpers.hpp"
Book::~Book(){
ids.erase(book_id_);
freed_ids.insert(book_id_);
//@TODO: To be implemented - destroy the object, not only deal with the book's ID!
};
Book::Book(Title title, Author author) : title_(title), author_(author){
if(!freed_ids.empty()){
book_id_ = *(freed_ids.begin());
}
else if(freed_ids.empty() && !ids.empty()){
book_id_ = *(ids.end());
}
else{
book_id_ = 0;
}
ids.insert(book_id_);
};
int main(){
std::cout << "Hello, from book.cpp!" << std::endl;
return EXIT_SUCCESS;
}
book.hpp
#ifndef book
#define book
#include "types.hpp"
#include "helpers.hpp"
/*
Class represeting a book as en entry in a collection of books.
@param author_ Author of the book.
@param title_ Title of the book.
@param book_id_ ID of a book.
*/
class Book{
private:
Author author_;
Title title_;
BookID book_id_;
public:
Book(Title title, Author author);
/*Get the title of the book.
@TODO: To be moved into a new class Library*/
Title get_title() const {return title_;}
/*Get the author of the book.
@TODO: To be moved into a new class Library*/
Author get_author() const {return author_;}
/*Get the ID number of the book.
@TODO: To be moved into a new class Library*/
BookID get_id() const {return book_id_;}
~Book();
};
#endif
只需将析构函数的主体移动到 .hpp 文件即可使代码编译正常,但这并不能解决我的问题. .cpp 文件中定义的构造函数工作得很好,我不明白为什么析构函数不起作用。
在此先感谢您的帮助!
P.S。如果我的代码中有什么地方没有意义而且很愚蠢,请随时告诉我,我将不胜感激。
@user17732522 我已经应用了你在下面的答案中建议我的更改,现在析构函数的问题没有发生,而是我收到错误消息,指出我使用的 std::set 对象有多个定义存储 ID 和名称 - 它们看起来像这样:
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
CMakeFiles/main.dir/objects.a(bookshelf.cpp.obj):C:/Users/Darek/Desktop/projects/myLibrary/include/helpers.hpp:9: multiple definition of `ids';
CMakeFiles/main.dir/objects.a(book.cpp.obj):C:/Users/Darek/Desktop/projects/myLibrary/include/helpers.hpp:9: first defined here
还有两个类似的错误与那个一起出现,除了它们指的是 freed_ids 和 shelf_names std::set 类型的对象。
这里是helpers.hpp文件,不是很复杂:
#ifndef HELPERS_HEADER_GUARD
#define HELPERS_HEADER_GUARD
#include <cstdlib>
#include <set>
#include <utility>
#include "types.hpp"
std::set<BookID> ids;
std::set<BookID> freed_ids;
std::set<ShelfName> shelf_names;
#endif
并且 bookshelf.cpp 文件也出现在错误消息中:
#include <iostream>
#include <vector>
#include <exception>
#include <algorithm>
#include "bookshelf.hpp"
Bookshelf::Bookshelf(ShelfName shelf_name){
if(std::find(shelf_names.begin(), shelf_names.end(), shelf_name) != shelf_names.end()){
throw(std::invalid_argument("A bookshelf with the given name already exists."));
}
else{
shelf_name_ = shelf_name;
// content_ = {};
}
}
您正在将您的程序分成多个:
add_executable(main main.cpp)
add_executable(book src/book.cpp include/bookshelf.hpp include/book.hpp include/types.hpp include/helpers.hpp)
add_executable(bookshelf src/bookshelf.cpp include/bookshelf.hpp)
您只需要一个可执行文件。此外,头文件不属于那里:
add_executable(main main.cpp src/book.cpp src/bookshelf.cpp)
(顺便说一句,为什么 main.cpp
不在 src
中?)
另一方面,include_directories
应该只包含 include
目录:
include_directories(
include
)
出现undefined reference错误的原因是,在你原来的配置下,CMake会创建三个独立的程序。只考虑这个:
add_executable(bookshelf src/bookshelf.cpp include/bookshelf.hpp)
它将通过编译 src/bookshelf.cpp
来构建,而不是 src/book.cpp
。 (.hpp
文件在这里无关紧要,但仍然不属于命令。)
所以bookshelf.cpp
可能包含一些Book
类型的(成员)变量。为了使用这个变量,程序需要知道如何构造和销毁该类型的变量。也就是说,它需要有Book
的构造函数和析构函数的定义。但是两者的定义只在文件book.cpp
中,不包含在this程序编译中