c/c++ 头文件的血案
头文件的血案
不小心在一个头文件里,加了函数的定义,结果导致编译时,提示这个函数被重复定义:(
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++ 头文件的血案的更多相关文章
- 【C】.h头文件的重复包含问题
.h头文件存在的意义就是封装,可以方便多个.c源文件使用,但要防止.h头文件被同一个.c源文件多次包含. 例如, io.h文件 #ifndef _IO_H_ #define _IO_H_ #defin ...
- C/C++头文件区别
在从C迁移到C++时,引用的头文件经常忘记是C的还是C++特有的 1. *.h limits.h ctype.h 2. c* climits cctype [结尾不含.h] 3. 其余的都属于C+ ...
- iOS开发中遇到的错误整理 - 集成第三方框架时,编译后XXX头文件找不到
iOS编译报错-XXX头文件找不到 错误出现的情况: 自己在继承第三方的SDK的时候,明明导入了头文件,但是系统报错,提示头文件找不到 解决方法 既然系统找不到,给他个具体路径,继续找去! 路径就填写 ...
- 使用powershell批量添加Keil和IAR的头文件路径
在Keil和IAR的工程中,为了使文件结构清晰,通常会设置很多的子文件夹,然后将头文件和源文件放在不同的子文件夹中,这样就需要手动添加这些头文件夹的路径.当工程结构非常复杂时,文件夹的数量就非常多,特 ...
- C/C++头文件使用 #ifndef #define #endif 的原因
背景 在编译的时候,出现"redefine"的错误,最后检查才发现对应的头文件没有写正确的预编译信息: #ifndef _HeadFileName_H #define _HeadF ...
- Qt - 错误总结 - 在自定义类头文件中添加Q_OBJECT 编译时报错(undefined reference to ‘vtable for xxThread)
错误提示:在添加的QThread子类头文件添加Q_OBJECT时,编译程序,出现"undefined reference to 'vtable for xxThread'"错误提示 ...
- C语言中,头文件和源文件的关系(转)
简单的说其实要理解C文件与头文件(即.h)有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程: 1.预处理阶段 2.词法与语法分析阶段 3.编译阶段,首先编译成纯汇编语句, ...
- 配置apue的头文件apue.h和unp的头文件anp.h
配置apue的头文件apue.h和unp的头文件anp.h 如果要使用gcc -g 来生成可调试文件一定要修改Make.defines.linux文件中的CFLAGS变量 修改为:CFLAGS=-an ...
- c/c++头文件_string
string, cstring, string.h 一.string头文件 主要包含一些字符串转换的函数 // sto* NARROW CONVERSIONS// sto* WIDE CONVERSI ...
随机推荐
- TDD in .NET Core - 简介
本文很多内容来自选自TDD实例一书. 预备知识 最好有一些预备知识,例如xUnit,Moq,如何编写易于测试的代码,这些内容我都写了文章:https://www.cnblogs.com/cgzl/p/ ...
- Python内置函数(22)——float
英文文档: class float([x]) Return a floating point number constructed from a number or string x. If the ...
- Centos 7 .Net core后台守护进程Supervisor配置
环境: Centos 7 已安装.Net core 2.0.0 .Net core 1.1.2 1.Supervisor安装 yum 安装 yum install supervisor (阿里云验证 ...
- 用abp vNext快速开发Quartz.NET定时任务管理界面
今天这篇文章我将通过实例代码带着大家一步一步通过abp vNext这个asp.net core的快速开发框架来进行Quartz.net定时任务调度的管理界面的开发.大伙最好跟着一起敲一下代码,当然源码 ...
- php的四个fetch语句
先给一个表 man: |---------------| |-name--|-age--| |--AA---|--aa---| |--BB---|--bb---| |--CC---|--cc---| ...
- EF架构~FluentValidation实体检验与实体分离了
回到目录 在MVC,EF,LINQ环境里,我们经常会用到DataModel(DO)和ViewModel(VO),可能对于它们的属性校验我们会采用特性的方式,当然这很直观,就连微软的DEMO也是如些,一 ...
- Python多版本共存(生产环境)
安装pyenv $ git clone git://github.com/yyuu/pyenv.git ~/.pyenv $ echo 'export PYENV_ROOT="$HOME/. ...
- synchronized学习
现代软件开发中并发已经成为一项基础能力,而Java精心设计的高效并发机制,正是构建大规模应用的基础之一.本文中我们将学习synchronized关键字的基本用法. synchronized是Java内 ...
- Django学习笔记(4)——Django连接数据库
前言 在MVC或者MTV设计模式中,模型(M)代表对数据库的操作.那么如何操作数据库呢?本小节就认真学习一下.首先复习一下Django的整个实现流程 ,然后再实现一下使用数据库的整个流程,最后学习一下 ...
- Linux之安装常用软件
Linux下安装软件的方法: 1,rpm(不推荐使用) 2,yum安装(使用快捷方便) 3,编译安装 一.安装python3(这里使用的是编译安装) 1,下载python3源码包 在centos下,第 ...