函数放在头文件中时被多次包含,编译报错:重定义


假设有头文件XLog.h

#pragma once
#include <string>
std::string LogTest(const std::string& szLog) { return szLog; }

Main函数包含头文件XLog.h,正常输出

控制台可以正常打印:

#include <iostream>
#include "XLog.h"
int main()
{
    std::cout << "main: " << LogTest("test") << std::endl;
}

结果:

main: test

添加PrintLog1.h头文件包含头文件XLog.h,正常输出

#pragma once
#include <iostream>
#include "XLog.h"
class CPrintLog1
{
public:
	CPrintLog1() {};
	virtual ~CPrintLog1(void) {};
	void Test1(const std::string& szLog) { std::cout << "Test1: " << LogTest(szLog) << std::endl;};
};
#include <iostream>
#include "XLog.h"
#include "PrintLog1.h"

int main()
{
    std::string szLog = "test";
    std::cout << "main: " << LogTest(szLog) << std::endl;
    CPrintLog1 objCPrintLog1;
    objCPrintLog1.Test1(szLog);
}

结果

main: test
Test1: test

添加PrintLog2.cpp文件包含头文件XLog.h,编译出错

PrintLog2.h

#pragma once
#include <iostream>
#include "XLog.h"
class CPrintLog2
{
public:
	CPrintLog2(){};
	virtual ~CPrintLog2(void){};
	void Test2(const std::string&);
};

PrintLog2.cpp

#include "PrintLog2.h"
#include "XLog.h"
void CPrintLog2::Test2(const std::string& szLog)
{std::cout << "Test2: " << LogTest(szLog) << std::endl;}

main.cpp

#include <iostream>
#include "XLog.h"
#include "PrintLog2.h"
int main()
{
    std::string szLog = "test";
    std::cout << "main: " << LogTest(szLog) << std::endl;
    CPrintLog2 objCPrintLog2;
    objCPrintLog2.Test2(szLog);
}

编译出错:
PrintLog2.obj : error LNK2005: “class std::basic_string<char,struct std::char_traits,class std::allocator > __cdecl LogTest(class std::basic_string<char,struct std::char_traits,class std::allocator > const &)” (?LogTest@@YA?AV? b a s i c s t r i n g @ D U ? basic_string@DU? basicstring@DU?char_traits@D@std@@V?$allocator@D@2@@std@@AEBV12@@Z) 已经在 ConsoleApplication.obj 中定义
1>G:\VS2019\ConsoleApplication\x64\Release\ConsoleApplication.exe : fatal error LNK1169: 找到一个或多个多重定义的符号

函数放在头文件中时被多次包含,编译出错原因

在链接时编译器不清楚要链接哪一个同名函数,因为每个.cpp的编译都是独立的,对于main.cpp和PrintLog2.cpp每个cpp来说,都包含了XLog的声明和实现( 找到了两个同名的函数,LogTest重定义)。

在头文件中使用了#pragma once或是#ifdef __xxx /#define __xxx/#endif也不能解决这种问题。

解决方法

方法一: 添加inline关键字:
inline函数在编译阶段展开,普通函数在运行阶段执行,所以编译时
函数的内容代码被直接解释到调用处,链接时相当于不存在这个函数,也就不存函数重定义的情况。
参考博客:C++之inline函数(内联函数)详解

#pragma once
#include <string>
inline std::string LogTest(const std::string& szLog){ return szLog;}

注意:
(1)内联函数只适用于功能简单的小函数(几行), 函数体中包含循环、switch等复杂语句的或语句比较多的函数,即便添加了inline,编译器也往往会忽略并将其作为普通函数处理。递归函数是不能被用来做内联函数的。
(2)函数声明和定义处都需要添加 inline 关键字,若只在声明处添加 inline ,编译器会忽略 inline 关键字,编译器视为普通函数。一般声明和实现都走头文件完成。

方法二: 添加static关键字,编译器链接时,只能连接到一份此函数,不存在重定义
参考博客:static修饰的函数作用与意义

#pragma once
#include <string>
static std::string LogTest(const std::string& szLog) { return szLog;}

方法三:XLog.h 中使用模板 template
参考博客: 关于C++编译链接和模板函数

#pragma once
#include <string>
#include <sstream>
template <typename T>
std::string LogTest(T t)
{
    std::stringstream ssLog;
    ssLog << t;
    return ssLog.str();
}

C++11及以上版本可实现输入不定个数参数

#pragma once
#include <string>
#include <sstream>

template <typename T>
void Print(std::stringstream& ss, T&& arg)
{
    ss << "[" << arg << "]";
}

template<typename ...Args>
std::string LogTest(Args&&... args)
{
    std::stringstream ssLog;
    int a[] = { (Print(ssLog,std::forward<Args>(args)), 0)... };
    return ssLog.str();
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qzy0621

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值