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

前言

本文首发于我的公众号:码农手札,主要介绍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++的编译链接过程,以及一些规则。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容