欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

c/c++ 头文件的血案

程序员文章站 2022-03-24 22:42:11
头文件的血案 不小心在一个头文件里,加了函数的定义,结果导致编译时,提示这个函数被重复定义:( Quote.h mainQuote.cpp 编译方法: 编译结果: shell multiple definition of c++ ifndef __QUOTE_H__ define __QUOTE_H ......

头文件的血案

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

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

c/c++ 头文件的血案

本人微信:xiaoshitou5854