首页 > 解决方案 > C ++奇怪的链接器错误,多重定义

问题描述

我正在尝试使用 makefile 在 C++ 中编译项目,当我只有 main.cpp 时它可以正常工作,但是一旦我添加了一些东西,它就会中断,我不知道为什么。请注意,它甚至无法识别 main。

错误图像(可能更适合阅读): 在此处输入图像描述

错误文本(以防万一):

make --jobs=9 build
mkdir -p "./dist" "./dist/obj"
g++ -std=c++17 -o dist/a.out dist/obj/hh.o dist/obj/main.o
dist/obj/main.o: In function `hh::hh()':
hh.cpp:(.text+0x0): multiple definition of `hh::hh()'
dist/obj/hh.o:hh.cpp:(.text+0x0): first defined here
dist/obj/main.o: In function `hh::hh()':
hh.cpp:(.text+0x0): multiple definition of `hh::hh()'
dist/obj/hh.o:hh.cpp:(.text+0x0): first defined here
dist/obj/main.o: In function `hh::b()':
hh.cpp:(.text+0xc): multiple definition of `hh::b()'
dist/obj/hh.o:hh.cpp:(.text+0xc): first defined here
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
make: *** [dist/a.out] Error 1
Makefile:12: recipe for target 'dist/a.out' failed

项目结构

root
├───.idea
├───assets
├───dist
│   └───obj
├───doc
├───examples
└───src

我的 Makefile 看起来像这样

COMPILER :=g++
CPPFLAGS :=-std=c++17
DIRS :=./dist

BUILD_DIR :=./dist
OBJS_DIR :=$(BUILD_DIR)/obj
SRC_DIR :=./src
EXEC_FILE :=a.out

# names of obj files (.o gets added later)
OBJS :=hh main

# uses linker to link everythink together
# using functions addsuffix and addprefix adding path and .o to the name of object 
# files specified in variable OBJS
$(BUILD_DIR)/$(EXEC_FILE): $(addsuffix .o, $(addprefix $(OBJS_DIR)/, $(OBJS)))
    $(COMPILER) $(CPPFLAGS) -o $@ $^

# recipe for any file in source files
$(OBJS_DIR)/%.o: $(SRC_DIR)/*.cpp
    $(COMPILER) $(CPPFLAGS) -c $< -o $@

# recipe for any nested file in source files
$(OBJS_DIR)/%.o: $(SRC_DIR)/*/*.cpp
    $(COMPILER) $(CPPFLAGS) -c $< -o $@

# generates dependecies into Makefile.d
deps:
    $(COMPILER) -MM $(SRC_DIR)/*.cpp > Makefile.d

-include Makefile.d

# Callable "scripts" for my IDE
.PHONY: dir_struct, clean, all, rebuild, build

all: dir_struct $(BUILD_DIR)/$(EXEC_FILE)

build: all

rebuild: clean dir_struct all

clean:
    rm -r dist

dir_struct:
    mkdir -p "$(BUILD_DIR)" "$(OBJS_DIR)"

只是一些仅用于测试的虚拟代码

// main.cpp
#include "hh.h"

int main() {
    hh g;
    g.b();
    return 0;
}

.

// hh.cpp
#include "hh.h"

hh::hh() = default;

void hh::b() {}

.

#ifndef HH_H
#define HH_H


class hh {
public:
    hh();
    void b();
};


#endif //HH_H

标签: c++makefilelinker

解决方案


$(OBJS_DIR)/%.o: $(SRC_DIR)/*.cpp
    $(COMPILER) $(CPPFLAGS) -c $< -o $@

在这里,您使用通配符来编译使用所有 cpp 文件作为依赖项的所有目标文件。这会导致每个 .o 文件仅包含列出的第一个 cpp 文件的定义,$(SRC_DIR)/*.cpp从而导致名称冲突。

您应该创建仅包含来自相应 cpp 文件的符号的 .o 文件,而不是这样做:

$(OBJS_DIR)/%.o: $(SRC_DIR)/%.cpp
    $(COMPILER) $(CPPFLAGS) -c $< -o $@

推荐阅读