今天面试碰到的一个以前没有想过的问题(顺便给一点分出去)
该题写的是考查应聘者对于C++中的存取权限的理解,但经我稍微的分析,觉得不完全如是,现把它公开出来,希望各位能够畅所欲言,发表自己的看法。
有类A,它被定义在a.h中,A有private成员i,如下:
class A {
private:
int i;
// ...
};
A的实现在a.cpp中(具体细节略)。在另外一个main.cpp中,我们使用了A,当然,我们知道直接访问A::i是不可能的。现在问题开始了,我们先把a.cpp编译成a.obj,然后修改a.h,把i改为public,然后我们再编译main.cpp,得到main.obj,然后把main.obj和a.obj放到一起去链接,问:这样做是否可以成功
我觉得这个问题很是有意思,因为根据以前接受过的正统熏陶,a.h被修改后,项目中的a.cpp肯定是要重新编译的,但这个题目没有这样做。所以让我很是费解了一番,自己胡乱写了一点上去,天知道是否正确与否:(
2 楼Frank001(Frank)回复于 2003-02-27 18:35:01 得分 5
我个人认为这样链接不能成功。
因为在编译a.cpp的时候, a.cpp里肯定有a的声明,要么直接写进去,要么用#include"a.h"包含进去,预处理的时候展开,然后编译,所以a.obj里的i是private的,这时改了a.h对a.obj是不起作用的,而main.cpp里展开后i是public的,所以链接是不会成功的。
7 楼HaiFen(小石头)回复于 2003-02-27 19:36:49 得分 10
不一定,有的编译器链接失败,因为它们把成员的访问属性也编码到函数名上了,改了访问属性后编码后的函数名也不一样,而且,有的编译器会把属于不同访问属性的成员的位置进行一定的调整,合并等等,运行后就会出错。
12 楼chinajiji(菜鸟叽叽)回复于 2003-02-27 20:22:23 得分 80
暂不考"虑继承,多重继承,虚拟多重继承,有虚拟函数"的情况下:
非静态成员可以link成功,静态成员与成员函数不能link成功.
因为静态成员与成员函数的名字在加入obj文件中的符号表时,一般会添加访问控制标识,而非静态成员在class中用偏移量表示,在生成对象时才分配实际空间,访问时通过对象的地址加上成员在class中的offset得到它的实际地址.也就是说在a.obj文件中没有非静态成员的空间分配,当然也没有相关的访问控制标识写入a.obj的符号表中,所以可以link成功.
24 楼ybco()回复于 2003-02-28 02:14:14 得分 5
看来各位对C++的Preprocessor的作用太不了解!!!
在编译a.obj时,a.h就已被Preprocessor拖进来,此后,任你怎么改a.h,都没用,a.obj “看不见”!!,除非重新编译。
所以,正确答案是:不行。
25 楼ckacka(/*小红帽*/ckacka();)回复于 2003-02-28 02:58:57 得分 10
一般情况下都是可以的!
如下:
//main.cpp
#include "class.h"
#include <iostream>
using namespace std;
int main(void)
{
CA* a = new CA;
a->i = 1;
cout << a->i << endl;
system("pause");
return 0;
}
//class.cpp
#include "class.h"
//class.h
class CA
{
public:
int i;
27 楼ybco()回复于 2003-02-28 04:22:58 得分 0
莫忘了你用的是开发公具来编译,所以能行。因为那工具会自动发现 a.h 的修改时间比a.obj新,它马上会重新编译 a.obj, 当然没问题了。这也实际开发中的情况。
原问题不是这样问的。
28 楼saucer(思归)回复于 2003-02-28 05:35:00 得分 15
1. a.h:
class A {
private:
int i;
public:
A (int x):i(x){}
void dummy();
};
2. a.cpp:
#include "a.h"
void A::dummy()
{
int xxx;
}
3. main.cpp:
#include "a.h"
#include <iostream>
using namespace std;
void main()
{
A a(123);
cout << a.i << endl;
cout << *((int*)&a) << endl;
}
c:/>cl /c a.cpp
.......modify "a.h" to have a public i
c:/>cl /c main.cpp
c:/>link main.obj a.obj
the last two steps can be combined:
c:/>cl main.cpp a.obj
and it runs:
c:/>main
123
123
here is a thread which discusses a similar problem:
34 楼Bandry(菜鸟-舍我其谁)回复于 2003-02-28 08:53:12 得分 10
应该是可以的,以前我的老总是微软ms操作系统开发部的一个小头目,他就说过public、private这些东西只是对我们程序员的限制,根本就不能保证数据的安全性,public、private数据编译以后在内存中没有区别,所以我觉得这两个obj肯定能链接成功,而且i也能被访问。
35 楼Tommy()回复于 2003-02-28 09:13:04 得分 10
同意saucer、earthharp(骄傲的石头) 、chinajiji(菜鸟叽叽)等的观点。
对于类的成员变量,一般都只是通过在类中的偏移值进行访问,并不会在符号表中生成一个符号项,也就不存在符号名称不同的问题了。只有静态成员变量才会在符号表中有入口。
只要编译器在public和private情况下不对变量在类中的位置进行调整就不会出错。C++一般只是保证在正常情况下不会误用,你问题可以用一些手段去绕过类型检查系统的。
不单是变量的访问权限修改后可以顺利连接,就算类的结构发生改变也是可以通过C++的编译的。有时我们使用了一个第三方提供的LIB文件,但是却用了一个错误版本的.H文件,程序可以顺利编译、连接,但是运行时却会失败,就是因为LIB文件和.H文件中的类的定义不同,但是C++却不能发现,但是访问的内容完全是错误的啦
54 楼fly_jason()回复于 2003-02-28 13:28:44 得分 0
我用VC6.0没有编译通过
class A
{
//private:
public:
void Output();
};
void A::Output()
{
cout << "A::Output" << endl;
}
main()
{
A a;
a.Output();
}
unresolved external symbol "public: void __thiscall A:
:Output(void)" (?Output@A@@QAEXXZ)
a.exe : fatal error LNK1120: 1 unresolved externalsTop
57 楼fengye()回复于 2003-02-28 16:59:34 得分 0
link是否成功估计是和编译器name decorate方法有关. 我试了这样的例子
a.h:
#ifndef A_H_INCLUDED
#define A_H_INCLUDED
#ifndef ACCESS
#define ACCESS private
#endif
class A {
ACCESS:
void fun();
static void sfun();
};
#endif
a.cpp:
#include "a.h"
#include <iostream>
void A::fun()
{
std::cout << "fun" << std::endl;
}
void A::sfun()
{
std::cout << "static fun" << std::endl;
}
main.cpp:
#define ACCESS public
#include "a.h"
int main()
{
A a;
a.fun();
A::sfun();
}
结果, bcc 5.5.1和gcc 3.2.2(mingw)可以, vc 7.1不可以(两个函数都不可以)
估计这也是implementation define的特性, 哪位有空到标准里去找找看 :)