利用同名类欺骗C++编译工具链(下)

上篇文章中说到了编译器和链接器在编译时所作的一些事情,其主要原因是链接器找不到需要的实现,同样的如果出现重复的实现也会出现问题,搞过C++的基本也都见过这个错误——重定义,一般来说编译器会报错,这就是当编译器找到两个相同修饰名的类或者函数时报的错误,这就是C++的一次定义规则。但是注意,这是编译器报出来的错误,不是链接器,也就是说同一个obj出现同修饰名是可以解决的,但是如果是不同obj文件,链接器是发现不了重名问题的。

假设有如下四个文件

A.h

#pragma once
#include <stdio.h>


int test();

class A
{
	char a;
public:
	A() { printf("A\n"); }
	~A() { printf("A\n"); }
};

B.h

#include <stdio.h>
class A
{
public:
	A() { printf("B\n"); }
	~A() { printf("B\n"); }
};

A.cpp

#include <stdio.h>
#include "B.h"

int test()
{
	A a;
	return 0;
}

main.cpp

#include <stdio.h>
#include "A.h"

int main()
{
	A a;
	test();
	return 0;
}

上述代码是可以轻松通过编译和成功链接的。

根据前一篇文章我们知道,编译它会生成两个目标文件a.obj(a.o)和main.obj(main.o),这两个目标文件中都包含一个叫A的类的实现。本来这个程序的预期输出应该是ABBA,但是我测试了VC和GCC这两个编译器,关掉所有的优化仍旧无法达到这个预期,在反汇编中可以看到他们生成的可执行文件都只有一份A的实现,但是在a.obj和main.obj的反汇编中分别找到了这两段代码,也就是说编译器不觉得编译过程有错误,每个obj都单独编译出来了,链接器也没觉得有问题,它把各个修饰名对应的可执行代码都找到了,具体执行出来可能有两个效果AAAA或BBBB,这取决于我们链接的顺序,简单的来说就是先到先得,在VC环境下

使用

link main.obj A.obj

生成的main.exe的执行结果就是AAAA

使用

link A.obj main.obj

生成的A.exe的执行结果就是BBBB

同样的事情有发生在linux的GCC和clang下。

因此实际编写程序时可能需要注意不要使用同名类,如果重定义还可以被编译器发现,如果是这种形式存在,可能连警告都不产生。

对于普通的类来说,当使用头文件声明,源文件定义这种方式时会出现重定义,但是如果定义写在头文件的类中就不会出现重定义的问题。但是对于模板类来说,无论函数定义放在哪里,都不会产生对应的重定义错误,这跟上一篇中说到的模板类的编译过程有关。

解决该问题有两种方案

1.使用namespace

2.使用类模板时,注意同名类的问题

发布了10 篇原创文章 · 获赞 12 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览