在C++中定义类析构函数时未定义的引用

2022-05-19 00:00:00 oop destructor c++

我正在做一个小项目来提高我在C++中的面向对象编程技能-该项目只是一个简单的书籍集合,类似于一个家庭图书馆,其中书籍可以存储在虚拟书架上,分组到更大的书架上,有各自的名称等,每本书都另外有它自己的ID,是一个正整数。我正在尝试创建一个用户定义的类析构函数,我想将析构函数的实现移到源文件中,遵循这样的规则,将比一行长的实现从.hpp文件移到.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:WINDOWSSystem32WindowsPowerShellv1.0powershell.exe -Command cmake --build Build" terminated with exit code: 1.

我使用Visual Studio代码并使用MinGW64编译器编译我的代码,在Windows 11上工作,我的项目是使用以下CMakeList文件设置的:

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文件中定义的构造函数工作正常,我不明白析构函数为什么不工作。

提前感谢您的帮助!

附注:如果我的代码中有一些东西没有意义,而且很愚蠢,也请随时告诉我,这样的英特尔将非常感激。

@user17732522我已经应用了您在下面的答案中建议我进行的更改,析构函数的问题现在不会出现,但我收到错误,指出我用于存储ID和名称的std::set对象有多个定义-它们如下所示:

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_id和搁板名称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
)

出现未定义引用错误的原因是,使用您的原始配置,CMake将创建三个独立的程序。只考虑这一点:

add_executable(bookshelf src/bookshelf.cpp include/bookshelf.hpp)
它将通过编译src/bookshelf.cpp构建,而不是使用src/book.cpp构建。(.hpp文件在这里不重要,但仍然不属于命令。)。

因此bookshelf.cpp可能包含一些Book类型的(成员)变量。为了使用该变量,程序需要知道如何构造和销毁该类型的变量。换句话说,它需要有Book的构造函数和析构函数的定义。但两者的定义仅在book.cpp文件中,此程序编译中不包含该文件。

相关文章