头文件的血案

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

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(Ubuntu) 下自然码加辅助码双拼输入的解决方案

    Linux(Ubuntu) 下自然码加辅助码双拼输入的解决方案 环境: Ubuntu 14.04 LTS 解决方案是 ibus-Rime 输入法, 再加上搭配自然码的配置表 (1) ibus 首先安装 ...

  2. Python - Python2与Python3的区别、转换与兼容

    区别 Python2.x与Python3.x版本区别:http://www.runoob.com/python/python-2x-3x.html 示例解读Python2和Python3之间的主要差异 ...

  3. Elasticsearch基础知识分享

    1. Elasticsearch背景介绍 Elasticsearch 是一个基于 Lucene 的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful web 接口.Elast ...

  4. 【Spark篇】---SparkStream初始与应用

    一.前述 SparkStreaming是流式处理框架,是Spark API的扩展,支持可扩展.高吞吐量.容错的实时数据流处理,实时数据的来源可以是:Kafka, Flume, Twitter, Zer ...

  5. .NET Core实战项目之CMS 第六章 入门篇-Vue的快速入门及其使用

    写在前面 上面文章我给大家介绍了Dapper这个ORM框架的简单使用,大伙会用了嘛!本来今天这篇文章是要讲Vue的快速入门的,原因是想在后面的文章中使用Vue进行这个CMS系统的后台管理界面的实现.但 ...

  6. vue+cordova构建跨平台应用集成并使用Cordova plugin

    安装 //安装 vue-cil npm install --global vue-cli //安装cordova npm i cordova -g cordova 新建项目 //新建cordova 项 ...

  7. Zuul介绍

    1.  Zuul是什么 Zuul是所有从设备和web站点到Netflix流媒体应用程序后端请求的前门.作为一个边缘服务应用程序,Zuul被构建来支持动态路由.监视.弹性和安全性.它还可以根据需要将请求 ...

  8. Android_ FragmentTabHost切换Fragment时避免重复加载UI

    使用FragmentTabHost时,Fragment之间切换时每次都会调用onCreateView方法,导致每次Fragment的布局都重绘,无法保持Fragment原有状态. 解决办法:在Frag ...

  9. Java基础18:Java序列化与反序列化

    更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...

  10. js反爬-从入门到精通webdriver

    学习JS反爬 地址:http://openlaw.cn/login.jsp 想在指导案例中抓些内容,需要登陆 账号密码发送会以下面方式发送 所以需要找到_csrf和加密后的password,_csrf ...