先看现象:
lee@xx:~/my_cpp/my_debug$ g++ main.o hello.o
lee@xx:~/my_cpp/my_debug$ ./a.out
hehe
hehe
lee@xx:~/my_cpp/my_debug$ g++ hello.o main.o
lee@xx:~/my_cpp/my_debug$ ./a.out
heihei
heihei
hello.cpp
#include "t2.h"
void test(){
A a;
a.hehe();
}
main.cpp
#include "t1.h"
void test();
int main(){
A a;
a.hehe();
test();
return 0;
}
t1.h
#include <iostream>
class A{
public:
A(){;}
void hehe(){
std::cout << "hehe"<<std::endl;
}
};
t2.h
#include <iostream>
class A{
public:
void hehe(){
std::cout << "heihei"<<std::endl;
}
};
试图解释这种现象:
g++ -E预处理一下main.cpp
# 3 "t1.h"
class A{
public:
A(){;}
void hehe(){
std::cout << "hehe"<<std::endl;
}
};
# 2 "main.cpp" 2
void test();
int main(){
A a;
a.hehe();
test();
}
预处理一下hello.cpp
# 2 "t2.h"
class A{
public:
void hehe(){
std::cout << "heihei"<<std::endl;
}
};
# 2 "hello.cpp" 2
void test(){
A a;
a.hehe();
}
可以确定两个版本的A分别编译进了main.o和hello.o了,链接时传入文件的先后顺序决定了哪个A会生效。
原来编译器对类函数的重复定义竟然是有如此的宽容度的。
这篇博客有讲到——
1是编译器的唯一命名规则,就是inline函数,class和模板类函数被多次包含的情况下,在编译的时候,编译器会自动把他们认为是同一个函数,不会发生二次定义的问题。前提是他们一模一样。
2是编译器会把class里面定义的函数当做inline函数,所以直接在类里面实现函数的定义没有关系。由上面的说明,他不会发生二次定义的问题。
3一般函数的声明和实现分开,在编译的时候,声明可以无数次,但是定义只能一份,只会生成一份函数的.obj,所以有函数调用的地方,编译器必须在调用的地方先保持现场,然后在花点时间去调用函数,然后回来,恢复现场。所以函数在头文件中实现,如果被包含二次。函数的实现就被编译了2次,如果单独写在一个.cpp中间,自然就编译成为一份.obj,不会产生二义性的问题。
3.inline函数在编译的时候直接复制在有该函数的地方,在空间上有消耗,但是在省去了时间上的消耗,是一个模板函数。也就是说在有这些函数的地方都不需要去调用函数,也就不涉及有2种函数可以调用产生的二义性问题。