① 在c++中,编译器是在什么阶段生成虚函数表的
运行时期的事怎么会和编译器有关系,要是那样的话所有运行程序的电脑不是都要装编译器了么。
② 表格框内虚线怎么做
以excel为例:
首先打开Excel文件,选中需要做成虚线的表格。
之后,找到菜单栏当中的格式,这一项,之后再找到单元格这一项,再找到当中的边框这一项。
然后,选择线型,在右边的线型栏中先选择虚线,这一种。
再选择当中的表格内线,即十字的那一种类。
之后,完成了,再看表格的内边线,就是虚线了。
③ 这个三联单的表格虚线怎么制作word里的虚线不一样啊,有人说这个虚线是软件自动生成的,那自己能做
在表格属性的“边框和底纹”-“边框”中选择圆点线,并在下面“宽度”中选择线点的大小磅值至合适大小就是了。
④ C/C++编译器中虚表是如何完成的
编译器会搜集一个类的所有虚函数,并在编译时生成一个虚函数表。然后编译器实际上会在类的构造和析构函数中加一些代码来达到初始化虚表指针和改变虚表指针的目的。
⑤ C++虚函数表的问题!(看一段代码,高分悬赏)
问题一
(int *) * (int *)(&b)和后面的*&p是不一样的。
(int *) * (int *)(&b)首先通过 (int *)(&b)一个指针,这个指针指向虚函数表。
然后“*(int *)(&b)”是指用*操作符间接引用这个地址,得到了虚函数表的首地址,也就是得到这么一个指针,这个指针指向了第一个虚函数的地址。
最后的那个(int *)做了个类型转换,这样才能调用cout <<;
这么理解更好:(int *) (* ((int*)(&b)) )。
并不是你理解的cout << b <<endl,关键就在于第二个 *起了间接引用的作用。
问题二
这个比较简单,根据c++语法规则,指向void的指针比较特殊,是不能间接引用的。
一个指向任何对象类型的指针都可以赋值给类型为void *的变量,void *可以显式转换到另一个类型,其他操作是不安全的,因为编译器不知道实际被指的的是哪种对象,要想使用void *,必须显式地将它转换成某个指向特定类型的指针。
例如
int i;
void *p = &i;
*p = 10; //错误,不能这么做。
p++;//错误
int *pi=static_cast<int *>(p);//正确
*pi = 10;
所以,你(void *)*(int *)(&b)没错,而(int *)*(void *)(&b)却错了,就在于第二个*做了间接引用。
问题三
p = fun;// p = &fun也可以,呵呵,why?
因为对一个函数无非就是两种操作,调用和取地址。所以可以省略从函数指针得到函数的间接运算符&,也可以省略取得函数地址的运算符&。
就是说
p=fun;//正确
p=&fun;//正确
p();//正确
(*p)();//正确
pFun = (Fun)*((int*)*(int*)(&b));前面的(Fun)*不知道是什么意思!!!
Fun是一个函数指针类型, (Fun)作用是做类型转换, *是间接引用运算符。
前面已经知道(int*)*(int*)(&b)是一个指针,这个指针指向了第一个虚函数的地址。
那么*((int*)*(int*)(&b))就是得到了第一个虚函数的地址,但是还不能直接使用,因为这里编译器会把它当成一个int型的数,必须加上(Fun)类型转换后,才可以赋值给pFun,否则编译器是会提示类型不匹配的。
同理触类旁通,举一反三,可以知道
pfun = (Fun)*(((int*)*((int*)(&b)))+1);//将第二个函数地址赋值给pfun
pfun = (Fun)*(((int*)*((int*)(&b)))+2);//将第三个函数地址赋值给pfun
我很羡慕你的学习态度,钻研的精神很好,以后如果有什么有趣的东西,大家一起探讨,共同进步。
⑥ DW里面怎么做虚线的表格。求具体步骤。
<html>
<head>
<title>这个是标题</title>
</head>>
<body>
<table style="border-style:dashed; border-width:1px; border-color:#000000;">虚线,黑色,1像素
<tr>
<td>表格里的内容</td>
</tr>
</table>
</body
</html>
希望可以帮到你!
⑦ g++编译器下关于虚基表指针
pb->f(); //因为pb指向的实际对象是A类,所以编译器会把A当作B的子类看待,由于函数同名,于是 pb->f()会查找A的虚函数表,而A没有虚函数表,于是出错 ,你可以把A的f()也定义成virtual 的,那就会调用A的f() ,或者把B的f()换个名字比如 f1(),那么pb->f();就不会查找虚函数表,而执行了A::f()
⑧ 编译器或者链接器会优化掉虚表吗
编译器能否优化掉虚表信息的关键点是“虚表是否有用”或者“是否是死代码”,如果编译器能够知道当前的目标文件作为main可执行体或者作为动态库,那么编译器就可以有选择地优化掉虚表信息。但是关键是编译器并不知道当前的文件是否是main可执行体,所以编译器会做保守选择,不去优化虚表信息。
从以下几个方面来看,编译器不太可能作虚表相关的优化:
1.通常情况下,编译器只能看到一个编译单元。编译器不能确定是否只有一个子类,或许几个月后心血来潮,你又添加了一个子类,编译然后和以前的目标文件链接在一起。
2.另外考虑到动态加载,在编译该文件很长时间之后,你仍然能够添加更多的子类。如果我是个编译器作者,我不会去冒险提供这样的优化。
3.另外完全没有必要。如果你担心虚函数的效率问题,你完全可以使用CRTP(curiously recurring template pattern)来实现静态多态。
虽然不可能优化掉虚表或者虚函数信息,但是编译器做到了另一项优化,就是在虚函数调用做到静态决议,如果当前虚调用能够在编译期确定,就无需获取对象地址赋给eax,然后再获取虚表地址或者调整对象首地址获得虚表地址,再在上面加上下标来jmp到正确的地址上去(其实,下标在编译时就是已经确定好的)。
⑨ C++虚表问题
虚函数表是编译器用来实现多态的方法。一般来说,虚函数表是一个数组,数组的元素是虚函数的指针。每个对象都会有一个虚函数表,当在某个对象上调用虚函数时,通过查询这个表来获得继承层次中到底哪个函数被实际调用。
对于一个类如果有虚函数,就会在这个类中创建一个虚表,也就会产生一个虚指针指向这个虚表。
既然有一个指针指向了虚表,这个类派生后,在派生类中就不必再创建虚表,如果派生类还有自己的虚函数,那么只在派生类中创建该虚函数的一个虚表,产生一个指向该虚表的指针。
为每个类设置虚表,初始化虚指针,为虚函数调用插入代码都是自动发生的,不必担心这些。
(我看到过虚继承下虚表问题的分析,直接继承下没看过,特此又分析了一下,修改)
#include<iostream>
using namespace std;
class A
{
public:
virtual void a(){};
};
class B:public virtual A
{
public:
virtual void b(){};
};
int main()
{
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
}
对于这个程序,你大概是在vc6.0下运行的吧,在vc下结果是4和12。我用的编译器是g++,对于这段程序的结果确实都为4.
因编译器不同导致结果不同这个我倒是没注意,这样可以得出,对虚函数这里的处理机制和编译器也是有关的,g++编译器还是更符合新的标准。
把虚继承virtual去掉再运行,在codeblocks下,g++编译运行结果都为4. vc下编译运行结果也都为4.
在分析一个例子:
#include<iostream>
using namespace std;
class A
{
char j[3];
public:
virtual void a(){};
};
class B:public A
{
char k[3];
public:
virtual void b(){};
};
class C:public B
{
char m[3];
public:
virtual void c(){};
};
int main()
{
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
cout<<sizeof(C)<<endl;
}
这个直接继承的例子,在vc下,codeblocks下结果都为 8 12 16.
再看这个直接继承的例子:
#include<iostream>
using namespace std;
class A
{
public:
virtual void a(){};
};
class B:public A
{
public:
virtual void b(){};
};
class C:public B
{
public:
virtual void c(){};
};
int main()
{
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
cout<<sizeof(C)<<endl;
}
vc下河codeblocks下结果都为 4. 这两个程序说明,直接继承下,输出结果应当是本类所占的字节加父类数据成员所占字节,父类的虚指针所占字节没有加上。
加入虚继承后:
#include<iostream>
using namespace std;
class A
{
char j[3];
public:
virtual void a(){};
};
class B:public virtual A
{
char k[3];
public:
virtual void b(){};
};
class C:public virtual B
{
char m[3];
public:
virtual void c(){};
};
int main()
{
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
cout<<sizeof(C)<<endl;
}
这段代码,在codeblocks下g++编译运行结果为:8 16 24.
在vc6.0下编译运行结果为:8 20 32.
能够分析出codeblocks下的结果是本类加上父类的结果。vc下是 本类加父类后又加了4个字节。
但是这段代码:
#include<iostream>
using namespace std;
class A
{
public:
virtual void a(){};
};
class B:public virtual A
{
public:
virtual void b(){};
};
class C:public virtual B
{
public:
virtual void c(){};
};
int main()
{
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
cout<<sizeof(C)<<endl;
}
codeblocks下编译运行结果为 4 4 4.
vc下编译运行结果为:4 12 20.
从结果来看,vc下是 本类加父类后又加了4个字节。而codeblocks结果就不再是本类加父类的结果(这个有点迷茫)。
⑩ C++中虚函数、虚表指针的问题
首先用gcc编译,gcc是c编译器,不会考虑多态的问题。
使用虚函数实际上就是利用虚函表实现确定函数的调用,在每个对象的存储空间中它会多出一定的存储空间,用于保存虚函数表的内存地址,所以你看到的不是12而是16,那4个字节用于保存虚函数表 (vftable virtual functon table)的内存起始地址。 虚函表的格式,不同的编译器不一样。但是在vc6.0中基本可以理解为
struct vftable {
func addr ; //第一个元素一定是相应函数的地址
//....// //这里呢还会多出来2个字长的数据,不知道做什么用的。
};
还有一种表叫虚基类表,这种更复杂了,它会在对象的存储空间,加入重复变量与基类变量的偏移。
观楼主英俊潇洒,风流倜傥,必当世豪杰,大侠闲暇之余,关注0x30网络贴吧,必成千秋霸业,建不世之功。