① 在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網路貼吧,必成千秋霸業,建不世之功。