golang 函数重载_面试深刨 —— 150分面重载
前言
重载在工程实践中的价值一直备受争议,却一直很受面试官青睐。本文不评论派系问题,只探讨主流语言的现状。主旨在于阐明重载的核心原理,助力面试;同时希望大家以后不要滥用重载。更多想法,可以评论、可以私聊,进一步探讨,此文尽量派系中立。
太长不看 —— 直击 special
- [30分] 重载的定义
- [60分] 重载的代码怎么写,c 、java、c、golang
- [100分] 重载的实现原理:符号改写
- [150分] 重载的运行机制 —— 编译、链接
- [180分] 进程内存空间,动态库加载机制(原因、利弊、必要性)
- [200分] 如何写出工程化代码,体现自己的工程师素养
我们熟悉的重载
直观定义:函数名相同,但参数不同的多个函数体,调用时,智能选择使用哪个函数
- 参数不同类型
void func_do_sth(int i) {
std::cout << "int func";
}
void func_do_sth(std::string s) {
std::cout << "string func";
}
func_do_sth(1); // 输出 int func
func_do_sth("1"); // 输出 string func;
- 参数不同个数
void func_do_sth(int i) {
std::cout << "int func";
}
void func_do_sth(int i, int j) {
std::cout << "int 2 func";
}
func_do_sth(1); // 输出 int func
func_do_sth(1, 2); // 输出 int 2 func
- 参数默认值
void func_do_sth(int i, int j=0) {
}
func_do_sth(1);
func_do_sth(1, 2);
如果是 java 如何实现上述功能?
- 参数不同类型
void func_do_sth_int(int i);
void func_do_sth_string(std::string s);
- 参数不同个数
void func_do_sth_1(int i);
void func_do_sth(int i, int j) ;
- 参数默认值
java 不支持这种语义
void func_do_sth(int i) {
func_do_sth(i, 0); // 转接到 func_do_sth(int i, int j);
}
void func_do_sth(int i, int j);
如果是 c 如何实现上述功能?
手工实现重载
void func_do_sth_int(int i);
void func_do_sth_string(std::string s);
void func_do_sth_1(int i);
void func_do_sth(int i, int j) ;
那么 golang 是怎么做的?
golang 采用 c 的思路,完全不支持重载。
func func_do_sth_int_1(i int) {}
func func_do_sth_int_2(i, j int) {}
What ?! 这么现代化的语言,竟然不支持这么经典的语法?!
是的,而且你应该注意到,java 还阉割了 c 的 default。
这其实是在编码便利与工程可维护性的一种妥协,只不过 golang 比较激进!代码在什么样的角度去取舍,可以衡量一个工程师的素养。c 、java、go 等语法开放程度,可以认为是语言设计者对使用者 “工程素养” 的信任程度,笔者更喜欢 golang。 这是一段插曲,2年以下工作经验尽量不要人云亦云地在面试中评论这些;比较有经验的可以视面试官能力探讨,这是一个很好的加分项(把握不好就是送命,就像不要在面试中讨论: 4个空格 vs Tab)。
回答完上面这些基本点,有可能针对此项的考查就结束了。你可以完美的拿到 60分!切记,这是在浪费机会!!
面试官想知道什么
为什么 c 做不到,c 能做到? 同样是一个 a.out 二进制,怎么 C 就可以重载,C 就不行呢。同样是 0101 你 C 凭什么得瑟!
那么我们就来看下 a.out 的表象
// a.cpp
void func_do_sth(int i) {
}
void func_do_sth(int i, int j) {
}
// b.c
void func_do_sth_1(int i) {
}
void func_do_sth_2(int i, int j) {
}
这段代码变成了什么
g -c a.cpp -o a.o
nm a.o # 输出 a.o 中的符号
------
0000000000000000 T __Z11func_do_sthi
0000000000000010 T __Z11func_do_sthii
gcc -c b.c -o b.o && nm b.o
------
0000000000000000 T _func_do_sth_1
0000000000000010 T _func_do_sth_2
__Z11func_do_sthi
这个鬼符是什么? 试试 c filt __Z11func_do_sthi
。 其实就是 _func_do_sth_1
,只不过编译器帮我们实现了区分。
g 相比于 gcc 更加智能(机械)地做了这个事情。—— 结论很简单,g 只是变了个魔术而已。
那么我们来看看 java
// a.java
class a{
void func_do_sth(int i) {}
void func_do_sth(int i, int j) {}
void test() {
func_do_sth(1);
func_do_sth(2, 4);
}
}
javac a.java; javap -verbose a.class |grep func_do_sth
----
#2 = Methodref #4.#17 // a.func_do_sth:(I)V
#3 = Methodref #4.#18 // a.func_do_sth:(II)V
java 编译器做了一个类似 g 的魔术,将 func_do_sth
进行了机械的包装。
java 不支持默认值,通过 _func_do_sth_1
调用 _func_do_sth_2
的思路来实现的,也就是利用了上面原理。
那么,c 默认值又是怎么回事
void func_do_sth(int i, int j=0) {}
nm a.o
-------
0000000000000000 T __Z11func_do_sthii
什么情况!没有 __Z11func_do_sthi
,那 func_do_sth(1)
可怎么活啊!!
void func_do_sth(int i, int j=2) {
}
void test() {
func_do_sth(1, 3);
func_do_sth(1);
}
g -S a.cpp -o a.s && cat a.s
----
movl $1, 您可能感兴趣的文章:
golang 函数重载_面试深刨 —— 150分面重载
mysql导入导出数据时中文乱码的解决办法
专家教你如何有效的学习Drupal - Drupal问答
js时间函数综合例子(日期计算、字符串转日期等)
php实现简单用户登录功能程序代码
asp.net常用http状态码表
MySQL中group_concat函数使用例子
linux下PostgreSQL的安装与使用
整理的.NET高效开发的25款工具【值得收藏】
网页标题随机显示名言js代码