You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

已加头文件保护仍出现链接器重复定义serialize.h函数问题

问题:头文件保护已添加仍出现链接器重复定义错误

我根据错误信息发现链接器似乎多次引入了serialize.h文件,但该文件已经添加了头文件保护:

#ifndef DIGIASSET_CORE_SERIALIZE_H
#define DIGIASSET_CORE_SERIALIZE_H

//all code here

#endif //DIGIASSET_CORE_SERIALIZE_H

我认为这段头文件保护代码应该能避免重复引入的问题,但还是出现了链接错误,请问哪里操作有误?

错误信息

/home/mctrivia/Programs/clion-2023.1.3/bin/cmake/linux/x64/bin/cmake --build /home/mctrivia/Code/CLionProjects/DigiAsset-Core/cmake-build-debug --target Google_Tests_run -j 18
[1/1] Linking CXX executable tests/Google_Tests_run
FAILED: tests/Google_Tests_run 
: && /usr/bin/c++ -std=c++11 -g -Wall -g  tests/CMakeFiles/Google_Tests_run.dir/BitIO.cpp.o tests/CMakeFiles/Google_Tests_run.dir/DatabaseChain.cpp.o tests/CMakeFiles/Google_Tests_run.dir/DigiByteCore.cpp.o tests/CMakeFiles/Google_Tests_run.dir/IPFS.cpp.o tests/CMakeFiles/Google_Tests_run.dir/Base58Tests.cpp.o tests/CMakeFiles/Google_Tests_run.dir/DigiAssetTest.cpp.o tests/CMakeFiles/Google_Tests_run.dir/DigiAssetRulesTest.cpp.o tests/CMakeFiles/Google_Tests_run.dir/DigiAssetTransactionTest.cpp.o tests/CMakeFiles/Google_Tests_run.dir/TestHelpers.cpp.o -o tests/Google_Tests_run  src/libdigiasset_core_lib.a  -ldigibyteapi  -lsqlite3  -lcryptopp  -lcurl  lib/libgtest.a  lib/libgtest_main.a  lib/libgtest.a && :
/usr/bin/ld: src/libdigiasset_core_lib.a(DigiAssetTypes.cpp.o): in function `std::vector<unsigned char, std::allocator<unsigned char> >::_S_nothrow_relocate(std::integral_constant<bool, true>)':
/home/mctrivia/Code/CLionProjects/DigiAsset-Core/src/serialize.h:22: multiple definition of `serialize(std::vector<unsigned char, std::allocator<unsigned char> >&, unsigned char const&)'; src/libdigiasset_core_lib.a(DigiAssetRules.cpp.o):/home/mctrivia/Code/CLionProjects/DigiAsset-Core/src/serialize.h:22: first defined here
/usr/bin/ld: src/libdigiasset_core_lib.a(DigiAssetTypes.cpp.o): in function `std::move_iterator<unsigned char*>::move_iterator(unsigned char*)':
/home/mctrivia/Code/CLionProjects/DigiAsset-Core/src/serialize.h:26: multiple definition of `deserialize(std::vector<unsigned char, std::allocator<unsigned char> > const&, unsigned long&, unsigned char&)'; src/libdigiasset_core_lib.a(DigiAssetRules.cpp.o):/home/mctrivia/Code/CLionProjects/DigiAsset-Core/src/serialize.h:26: first defined here
/usr/bin/ld: src/libdigiasset_core_lib.a(DigiAssetTypes.cpp.o): in function `serialize(std::vector<unsigned char, std::allocator<unsigned char> >&, unsigned long const&)':
/home/mctrivia/Code/CLionProjects/DigiAsset-Core/src/serialize.h:32: multiple definition of `serialize(std::vector<unsigned char, std::allocator<unsigned char> >&, unsigned long const&)'; src/libdigiasset_core_lib.a(DigiAssetRules.cpp.o):/home/mctrivia/Code/CLionProjects/DigiAsset-Core/src/serialize.h:32: first defined here
/usr/bin/ld: src/libdigiasset_core_lib.a(DigiAssetTypes.cpp.o): in function `deserialize(std::vector<unsigned char, std::allocator<unsigned char> > const&, unsigned long&, unsigned long&)':
/home/mctrivia/Code/CLionProjects/DigiAsset-Core/src/serialize.h:39: multiple definition of `deserialize(std::vector<unsigned char, std::allocator<unsigned char> > const&, unsigned long&, unsigned long&)'; src/libdigiasset_core_lib.a(DigiAssetRules.cpp.o):/home/mctrivia/Code/CLionProjects/DigiAsset-Core/src/serialize.h:39: first defined here
/usr/bin/ld: src/libdigiasset_core_lib.a(DigiAssetTypes.cpp.o): in function `serialize(std::vector<unsigned char, std::allocator<unsigned char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)':
/home/mctrivia/Code/CLionProjects/DigiAsset-Core/src/serialize.h:50: multiple definition of `serialize(std::vector<unsigned char, std::allocator<unsigned char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'; src/libdigiasset_core_lib.a(DigiAssetRules.cpp.o):/home/mctrivia/Code/CLionProjects/DigiAsset-Core/src/serialize.h:50: first defined here
/usr/bin/ld: src/libdigiasset_core_lib.a(DigiAssetTypes.cpp.o): in function `deserialize(std::vector<unsigned char, std::allocator<unsigned char> > const&, unsigned long&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)':
/home/mctrivia/Code/CLionProjects/DigiAsset-Core/src/serialize.h:60: multiple definition of `deserialize(std::vector<unsigned char, std::allocator<unsigned char> > const&, unsigned long&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)'; src/libdigiasset_core_lib.a(DigiAssetRules.cpp.o):/home/mctrivia/Code/CLionProjects/DigiAsset-Core/src/serialize.h:60: first defined here
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.

完整serialize.h文件

//
// Created by mctrivia on 06/07/23.
//

#ifndef DIGIASSET_CORE_SERIALIZE_H
#define DIGIASSET_CORE_SERIALIZE_H

#include <vector>
#include <cstdint>
#include <cstddef>
#include <string>
#include <stdexcept>

using namespace std;

/**
 * Serialize function.  Takes input serializes it and adds it to the serializedData
 */
void serialize(vector<uint8_t>& serializedData, const uint8_t& input) {
    serializedData.push_back(input);
}

void deserialize(const vector<uint8_t>& serializedData, size_t& i, uint8_t& output) {
    if (i >= serializedData.size()) throw out_of_range("read past end of data");
    output = serializedData[i];
    i++;
}

void serialize(vector<uint8_t>& serializedData, const uint64_t& input) {
    for (size_t shift = 56; shift > 0; shift -= 8) {
    serializedData.push_back((input >> shift) & 0xff);
    }
    serializedData.push_back(input & 0xff);
}

void deserialize(const vector<uint8_t>& serializedData, size_t& i, uint64_t& output) {
    if (i + 8 > serializedData.size()) throw out_of_range("read past end of data");
    output = 0;
    for (size_t shift = 56; shift > 0; shift -= 8) {
    output += ((uint64_t) serializedData[i] << shift);
    i++;
    }
    output += serializedData[i];
    i++;
}

void serialize(vector<uint8_t>& serializedData, const string& input) {
    //store number of elements
    serialize(serializedData, (uint64_t) input.size());

    //store elements
    for (const char& letter: input) {
    serialize(serializedData, (uint8_t) letter);
    }
}

void deserialize(const vector<uint8_t>& serializedData, size_t& i, string& output) {
    //get length
    uint64_t size;
    deserialize(serializedData, i, size);

    //error check
    if (i + size > serializedData.size()) throw out_of_range("read past end of data");

    //decode element
    output.clear();
    output.resize(size);
    for (size_t ii = 0; ii < size; ii++) {
    output[ii] = serializedData[i];
    i++;
    }
}

/**
 * Generic function to serialize vector of any type
 * Still need a function to serialize the type inside the vector for this to work
 */
template<typename T>
void serialize(vector<uint8_t>& serializedData, const vector<T>& input) {
    //store number of elements
    serialize(serializedData, (uint64_t) input.size());

    //store elements
    for (const T& element: input) {
    serialize(serializedData, element);
    }
}


template<typename T>
void deserialize(const vector<uint8_t>& serializedData, size_t& i, vector<T>& output) {
    //get length
    uint64_t size;
    deserialize(serializedData, i, size);

    //decode element
    output.clear();
    output.resize(size);
    for (T& value: output) {
    deserialize(serializedData, i, value);
    }
}


#endif //DIGIASSET_CORE_SERIALIZE_H
解答
  • 头文件保护(#ifndef/#define/#endif)的作用是避免单个编译单元内重复包含头文件,但解决不了链接阶段的重复定义问题。你遇到的问题本质是:多个.cpp文件(比如DigiAssetTypes.cpp和DigiAssetRules.cpp)都包含了serialize.h,每个编译单元都会生成这些非模板函数的定义副本,链接时就会报“multiple definition”错误。

  • 解决方法有三种:

    1. 分离声明与实现:在serialize.h中只保留函数声明,把实现移到新的serialize.cpp文件中,然后编译这个cpp文件并加入到库或可执行文件的构建中。
      示例声明:
      void serialize(vector<uint8_t>& serializedData, const uint8_t& input);
      void deserialize(const vector<uint8_t>& serializedData, size_t& i, uint8_t& output);
      // 其他函数同理
      
    2. 添加inline关键字:在头文件中的函数定义前加上inline,这样编译器会确保每个编译单元内的函数定义只会生成一份可执行代码,链接时就不会冲突。
      示例:
      inline void serialize(vector<uint8_t>& serializedData, const uint8_t& input) {
          serializedData.push_back(input);
      }
      
    3. 放入匿名命名空间(不推荐):在头文件中把非模板函数包裹在namespace {}里,但这会导致每个编译单元生成独立的函数副本,增加可执行文件体积,不符合代码规范。
  • 注意:模板函数不需要处理,因为模板是在编译实例化时生成代码,不会触发重复定义问题,你的代码中模板部分是正常的。

内容的提问来源于stack exchange,提问作者Matthew Cornelisse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 15:28:13