美文网首页
为什么cpp的声明和实现要放在一个文件

为什么cpp的声明和实现要放在一个文件

作者: lwj_ow | 来源:发表于2019-11-16 10:13 被阅读0次

前言

本文首发于我的公众号:码农手札,主要介绍linux下c++开发的知识包括网络编程的知识同时也会介绍一些有趣的算法题,欢迎大家关注,利用碎片时间学习一些编程知识,冰冻三尺非一日之寒,让我们一起加油!

说实话,我个人是不怎么使用模板的,使用场景很少,因此在上一篇博客里面难得用了一次模板,反而出了点小问题,我遇到的问题就是把声明和实现分开在h文件和cpp文件,因此在编译的时候怎么都无法通过,简直懵逼了,不过好在上网搜了一下突然想起来好像之前看到过模板的声明和实现必须都写在一个头文件中,这里我们来简单研究下为什么cpp里面,模板和声明是必须放在一起的。

测试代码

让我们先写一份非模版实现的代码,然后再写一份使用模板实现的代码,使用一些辅助工具来看看有什么不同,下面先展示一份非模板实现的代码

///test.h
#pragma once

 class Test {
public:
  Test(const int &t) : ele_(t) {}
  int get_ele();

private:
  int ele_;
};

///test.cpp
#include "test.h"
int Test::get_ele() { return ele_; }

///test_main.cpp
#include "tetest_
#include <iostream>

int main(int argc, char const *argv[]) {
  Test test(5);
  std::cout << test.get_ele() << std::endl;
  return 0;
}

下面是使用模板实现的代码

///test_template.h
#pragma once

template <typename T> class TestTemplate {
public:
  TestTemplate(const T &t) : ele_(t) {}
  T get_ele();

private:
  T ele_;
};

///test_template.cpp
#include "test_template.h"
template <typename T> T TestTemplate<T>::get_ele() { return ele_; }

///main.cpp
#include "test_template.h"
#include <iostream>

int main(int argc, char const *argv[]) {
  TestTemplate<int> test_template(5);
  std::cout << test_template.get_ele() << std::endl;
  return 0;
}

非模板代码编译结果

很显然,按照c++的规则,前一种实现方式是完全没有问题的,但是后一种将模板声明和实现分开的方式编译是无法通过的,那么让我们试试看看到底是什么情况

g++ -c test.cpp

获得test.o文件,然后使用nm来查看里面有什么函数

nm test.o

在我电脑上,输出为0000000000000000 T __ZN4Test7get_eleE,这个时候,我们能够猜到这个就是get_ele这个函数的定义,其实这个新的名字是c++的一种给函数定义唯一标示的一种方式,这个是根据命名空间以及函数长度包括参数类型生成的,有一定的规则,不过这个其实没必要太关注(这里我多提一句,因为生成新名字的时候并不包括返回值的类型,这就是c++要求重载函数必须要求函数参数不同的原因)。不过当我们在遇到程序bug的时候,使用gdb也会看到这个东西,这个时候我们需要查看这个函数,这里我介绍一个辅助工具查看函数实际的名字

c++ __ZN4Test7get_eleE

这个命令会告诉我们那个名字对应的真实名字,我这里对应的是Test::get_ele(),不多提,也就是说Test::get_ele()的实现是在test.o文件里面的,当执行到编译的第四个阶段的时候也就是链接,test_main.o会告诉链接器,它需要Test::get_ele()的定义,这个时候链接器发现,哎,test.o文件里面,那正好,然后顺利生成可执行文件,到这里都是顺理成章,那么,看起来我们可以猜测将模板类的声明和实现分开的话,是否链接器无法找到它想要的东西呢,让我们继续下一步。

模板代码编译尝试

这里,毋庸置疑,直接编译肯定是会失败的,那么让我们来看看,到底是哪里出了问题,根据编译报错,Undefined symbols for architecture x86_64: "TestTemplate<int>::get_ele()", referenced from: _main in template_main-7858f0.o, 这个编译错误已经很明显的告诉我们,链接器没找到TestTemplate<int>::get_ele()的定义,那么我们来看看test_template.o里面是否有TestTemplate<int>::get_ele()的定义

g++ -c test_template.cpp

获得test_template.o文件,然后我们用老办法,使用nm来看看test_template.o里面有没有我们想要的东西

nm test_template.o

终端什么都没输出,不过我们对于这个结果也不意外,因为要是有定义才奇怪了,这里我来解释一下,为什么没有定义呢,因为c++标准规定,如果当一个模板没有被用到的时候,它就不该被实现出来,这里由于test_template.cpp里面并没有任何地方用到了TestTemplate<int>,所以自然也不会被实例化,这里可能有人会奇怪,但是main函数里面不是调用了吗,这里需要注意的是c++的编译方式,各个cpp文件是相互独立的,所以如果test_template.cpp里面没有用到,其他任何一个cpp文件用到,模板是都不会被实例化的,这也就是为什么我们使用nm命令查看test_template.o却什么也没看到的原因。

换种方式

我们将test_template.cpp改成下面这样

#include "test_template.h"

int test() {
  TestTemplate<int> t(5);
  return t.get_ele();
}

template <typename T> T TestTemplate<T>::get_ele() { return ele_; }

然后我们再编译,这个时候我们就会惊奇的发现,编译完全正常,这里我们仅仅添加了一个无法被外界调用的test函数(因为我们并没有在头文件种声明test函数),但是正是由于test函数中用到了TestTemplate<int>,因此模板就被实例化了,如果我们将test函数中模板类型从int改成double,再次编译肯定是无法通过的,这个我就不解释了。

总结

到这里,文章就写的差不多了,这篇文章其实没啥很难的地方,本质上其实就是说c++要求模版声明和实现放在一个头文件,不过这里介绍了几个小命令,让我们熟悉一下c++的编译链接过程,以及一些规则。

相关文章

  • C++分文件定义类

    类的声明存放在了头文件(.h,头文件也用来声明变量)里,函数(方法)的实现放在了.cpp文件里,.cpp文件...

  • 为什么cpp的声明和实现要放在一个文件

    前言 本文首发于我的公众号:码农手札,主要介绍linux下c++开发的知识包括网络编程的知识同时也会介绍一些有趣的...

  • C++类的注意事项

    类的声明放在头文件中,类的实现放到cpp文件中 如果要把一个类的函数定义了内联函数,可以在声明中直接定义,或者在实...

  • 高质量C++编程指南 ----文件结构

    1.文件结构 头文件(.h):保存程序的声明 定义文件(.cpp):保存程序的实现 1.1 版权和版本的声明 (1...

  • 代码组织方式

    以往自己写代码,都是将函数声明、类声明和定义等写到 .h文件,将函数定义、类实现等写到 .cpp 文件。最近发现很...

  • JNI——C++基础一

    创建一个C++项目: 把头文件中构造函数和析构函数的具体实现放在cpp文件中,这里的public和private跟...

  • C++学习笔记(二)头文件与定义文件

    1、C++/C程序文件结构:头文件(.h文件,用于保存程序的声明)+定义文件(.cpp/.c文件,程序的实现) 2...

  • 黑马C++视频笔记《模板:类模板实战案例》

    类模板声明及实现 其中,类的声明以及实现均放在一个文件udfArray.hpp中: 测试

  • c 关于全局函数加不加extern的问题

    首先把声明部分放头文件里,然后把定义放在.c .cpp之类的文件中(定义中也要引入该头文件)。需要这个函数的时候引...

  • VAssistX 常用快捷键

    函数跳转 Alt + G - 函数定义和声明的跳转Alt + O - 在.h与.cpp文件中实现相互转换Alt +...

网友评论

      本文标题:为什么cpp的声明和实现要放在一个文件

      本文链接:https://www.haomeiwen.com/subject/akzeictx.html