头文件的血案

不小心在一个头文件里,加了函数的定义,结果导致编译时,提示这个函数被重复定义:(

Quote.h

#ifndef __QUOTE_H__
#define __QUOTE_H__ #include <iostream> class Quote{
public:
Quote() = default;
Quote(const std::string& book, double pri)
:bookNo(book), price(pri){}
std::string isbn() const{return bookNo;}
virtual double net_price(std::size_t n)const{
return n * price;
}
virtual ~Quote() = default;
private:
std::string bookNo;
protected:
double price = 0.0;
}; class Bulk_quote : public Quote{
public:
Bulk_quote() = default;
Bulk_quote(const std::string&, double, std::size_t,
double);
double net_price(std::size_t n)const override;
private:
std::size_t min_qty = 0;//适用于折扣的最低购买数量
double discount = 0.0;//折扣额
}; //不小心在这个头文件里,加了下面这个函数的定义,结果导致编译时,提示这个函数被重复定义:(
double print_total(std::ostream& os,
const Quote& item, size_t n){
double ret = item.net_price(n);
os << "ISBN: " << item.isbn()
<< " # sold: " << n << " total due: " << ret << std::endl;
return ret; #endif

Quote.cpp

#include "Quote.h"

Bulk_quote::Bulk_quote(const std::string& book, double p,
std::size_t qty, double disc):
Quote(book, p), min_qty(qty), discount(disc){} double Bulk_quote::net_price(std::size_t cnt)const {
if(cnt >= min_qty){
return cnt * (1 - discount) * price;
}
else{
return cnt * price;
}
}

mainQuote.cpp

#include "Quote.h"

int main(){
Quote q("01",100.5);
double d = print_total(std::cout, q, 1); Bulk_quote bq("01", 100.5, 5, 1);
}

编译方法:

g++ -g Quote.cpp mainQuote.cpp -std=c++11

编译结果:

multiple definition of `print_total(std::ostream&, Quote const&, unsigned long)`

错误原因,大概知道了,但是不知道为什么

  • 因为在Quote.cpp里包含了Quote.h,在mainQuote.cpp里也包含了Quote.h,自己错的的认为:在头文件的开头只要加了保护 #ifndef _QUOTE_H_,就不会把Quote.h包含2次,导致函数重复定义。但是没有什么卵用,这是为什么呢??? 用g++ -E Quote.cpp mainQuote.cpp > main.i,生成了预编译后的文件main.i,这个文件里确实有2个地方有print_total的定义。
  • 网上说,最好不要在头文件里定义函数和变量,否则会导致重复定义,看来网上说的还真对!但是如果把函数print_total定义为inline函数的话,编译就没有问题。这又是为什么呢???

修改方案1,把函数放到Quote.cpp里

Quote.h

#ifndef __QUOTE_H__
#define __QUOTE_H__ #include <iostream> class Quote{
public:
Quote() = default;
Quote(const std::string& book, double pri)
:bookNo(book), price(pri){}
std::string isbn() const{return bookNo;}
virtual double net_price(std::size_t n)const{
return n * price;
}
virtual ~Quote() = default;
private:
std::string bookNo;
protected:
double price = 0.0;
}; class Bulk_quote : public Quote{
public:
Bulk_quote() = default;
Bulk_quote(const std::string&, double, std::size_t,
double);
double net_price(std::size_t n)const override;
private:
std::size_t min_qty = 0;//适用于折扣的最低购买数量
double discount = 0.0;//折扣额
};
#endif

Quote.cpp

#include "Quote.h"

Bulk_quote::Bulk_quote(const std::string& book, double p,
std::size_t qty, double disc):
Quote(book, p), min_qty(qty), discount(disc){} double Bulk_quote::net_price(std::size_t cnt)const {
if(cnt >= min_qty){
return cnt * (1 - discount) * price;
}
else{
return cnt * price;
}
} double print_total(std::ostream& os,
const Quote& item, size_t n){
double ret = item.net_price(n);
os << "ISBN: " << item.isbn()
<< " # sold: " << n << " total due: " << ret << std::endl;
return ret;
}

mainQuote.cpp

#include "Quote.h"

int main(){
Quote q("01",100.5);
double d = print_total(std::cout, q, 1); Bulk_quote bq("01", 100.5, 5, 1);
}

编译结果:

error: ‘print_total’ was not declared in this scope

错误原因,Quote.h里没有函数print_total的声明和定义,所以当然出这个错误

修改方法:加一个函数print_total的声明,就好了。

mainQuote.cpp

#include "Quote.h"

double print_total(std::ostream& os,
const Quote& item, size_t n);
int main(){
Quote q("01",100.5);
double d = print_total(std::cout, q, 1); Bulk_quote bq("01", 100.5, 5, 1);
}

从上面2次错误的经验,感受到了,c++编译器的脾气了。

编译阶段:编译器编译到某个函数print_total调用的地方时,就要去前面找这个函数的声明或者定义,如果只有声明没有定义,就先把定义的地方预留出来。

链接阶段:把在编译阶段预留出函数print_total的定义的空缺,填补上。(print_total的定义在文件Quote.cpp里)

c/c++ 学习互助QQ群:877684253

本人微信:xiaoshitou5854

c/c++ 头文件的血案的更多相关文章

  1. 【C】.h头文件的重复包含问题

    .h头文件存在的意义就是封装,可以方便多个.c源文件使用,但要防止.h头文件被同一个.c源文件多次包含. 例如, io.h文件 #ifndef _IO_H_ #define _IO_H_ #defin ...

  2. C/C++头文件区别

    在从C迁移到C++时,引用的头文件经常忘记是C的还是C++特有的 1. *.h   limits.h ctype.h 2. c* climits cctype [结尾不含.h] 3. 其余的都属于C+ ...

  3. iOS开发中遇到的错误整理 - 集成第三方框架时,编译后XXX头文件找不到

    iOS编译报错-XXX头文件找不到 错误出现的情况: 自己在继承第三方的SDK的时候,明明导入了头文件,但是系统报错,提示头文件找不到 解决方法 既然系统找不到,给他个具体路径,继续找去! 路径就填写 ...

  4. 使用powershell批量添加Keil和IAR的头文件路径

    在Keil和IAR的工程中,为了使文件结构清晰,通常会设置很多的子文件夹,然后将头文件和源文件放在不同的子文件夹中,这样就需要手动添加这些头文件夹的路径.当工程结构非常复杂时,文件夹的数量就非常多,特 ...

  5. C/C++头文件使用 #ifndef #define #endif 的原因

    背景 在编译的时候,出现"redefine"的错误,最后检查才发现对应的头文件没有写正确的预编译信息: #ifndef _HeadFileName_H #define _HeadF ...

  6. Qt - 错误总结 - 在自定义类头文件中添加Q_OBJECT 编译时报错(undefined reference to ‘vtable for xxThread)

    错误提示:在添加的QThread子类头文件添加Q_OBJECT时,编译程序,出现"undefined reference to 'vtable for xxThread'"错误提示 ...

  7. C语言中,头文件和源文件的关系(转)

    简单的说其实要理解C文件与头文件(即.h)有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程: 1.预处理阶段 2.词法与语法分析阶段 3.编译阶段,首先编译成纯汇编语句, ...

  8. 配置apue的头文件apue.h和unp的头文件anp.h

    配置apue的头文件apue.h和unp的头文件anp.h 如果要使用gcc -g 来生成可调试文件一定要修改Make.defines.linux文件中的CFLAGS变量 修改为:CFLAGS=-an ...

  9. c/c++头文件_string

    string, cstring, string.h 一.string头文件 主要包含一些字符串转换的函数 // sto* NARROW CONVERSIONS// sto* WIDE CONVERSI ...

随机推荐

  1. linux下crontab的使用

    在LINUX中,周期执行的任务一般由cron这个守护进程来处理[ps -ef|grep cron].cron读取一个或多个配置文件,这些配置文件中包含了命令行及其调用时间.cron的配置文件称为“cr ...

  2. Feign源码解析系列-核心初始化

    开始 初始化Feign客户端当然是整个过程中的核心部分,毕竟初始化完毕就等着调用了,初始化时候准备的什么,流程就走什么. 内容 从上一篇中,我们已经知道,对于扫描到的每一个有@FeignClient, ...

  3. IntelliJ IDEA下SVN的配置及使用说明

    1 下载及安装SVN客户端. 到官网下载小乌龟SVN客户端,官网地址:https://tortoisesvn.net/downloads.html,根据操作系统情况选择适合版本.比如64为操作系统,如 ...

  4. 拥抱单页网站! jQuery全屏滚动插件fullPage.js

    不知道从什么时候开始,单页网站就悄悄走进人们的视线,尤其是国外的网站,更是钟爱单页网站.制作一个全屏滚动的效果,然后每个滚动页弄一个好看的背景色,配上一些描述性的文字,大家都喜欢这么弄,仿佛逼格瞬间可 ...

  5. 【Java基础】【21IO(字符流)&字符流其他内容&递归】

    21.01_IO流(字符流FileReader) 1.字符流是什么 字符流是可以直接读写字符的IO流 字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写 ...

  6. leetcode — path-sum

    /** * Source : https://oj.leetcode.com/problems/path-sum/ * * * Given a binary tree and a sum, deter ...

  7. Jenkins+Maven+Gitlab+Tomcat 自动化构建打包、部署

    一.环境需求 本帖针对的是Linux环境,Windows或其他系统也可借鉴.具体只讲述Jenkins配置以及整个流程的实现. 1.JDK(或JRE)及Java环境变量配置,我用的是JDK1.8.0_1 ...

  8. RabbitMQ基础系列--客户端开发

    Ⅰ.高层接口 ConnectionFactory Connection Channel Consumor Ⅱ.操作流程及API [一]创建连接工厂ConnectionFactory Connectio ...

  9. 消息队列_MSMQ(2)简单应用

    上一篇讲了MSMQ的简单知识,那这次我们讲下简单代码的知识 附上源码: https://gitee.com/592576605/MSMQ_HANS 下面是简单的类库说明,具体咋用就看源码吧 类(Cla ...

  10. Android APK 签名文件MANIFEST.MF、CERT.SF、CERT.RSA分析

    首先我们找一个已经签名的apk文件,修改后缀名为zip,然后解压.可以看到里面有一个META-INF文件夹,里面就是签名验证的文件.有三个文件MANIFEST.MF.CERT.SF.CERT.RSA分 ...