Jquery中文网 www.jquerycn.cn
Jquery中文网 >  后端编程  >  Go语言  >  正文 golang 函数重载_面试深刨 —— 150分面重载

golang 函数重载_面试深刨 —— 150分面重载

发布时间:2021-05-15   编辑:www.jquerycn.cn
jquery中文网为您提供golang 函数重载,面试深刨 —— 150分面重载等资源,欢迎您收藏本站,我们将为您提供最新的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) 可怎么活啊!!

[关闭]