當前位置:首頁 » 編程語言 » c語言虛函數

c語言虛函數

發布時間: 2022-07-02 16:01:49

『壹』 為什麼C++的構造函數不可以是虛函數,而析構

1、為什麼構造函數不可以是虛函數
①從存儲空間角度

虛函數對應一個vtable,這大家都知道,可是這個vtable其實是存儲在對象的內存空間的。問題出來了,如果構造函數是虛的,就需要通過 vtable來調用,可是對象還沒有實例化,也就是內存空間還沒有,無法找到vtable,所以構造函數不能是虛函數。

②從使用角度

虛函數主要用於在信息不全的情況下,能使重載的函數得到對應的調用。構造函數本身就是要初始化實例,那使用虛函數也沒有實際意義呀。所以構造函數沒有必要是虛函數。

虛函數的作用在於通過父類的指針或者引用來調用它的時候能夠變成調用子類的那個成員函數。而構造函數是在創建對象時自動調用的,不可能通過父類的指針或者引用去調用,因此也就規定構造函數不能是虛函數。

③構造函數不需要是虛函數,也不允許是虛函數,因為創建一個對象時我們總是要明確指定對象的類型,盡管我們可能通過實驗室的基類的指針或引用去訪問它。但析構卻不一定,我們往往通過基類的指針來銷毀對象。這時候如果析構函數不是虛函數,就不能正確識別對象類型從而不能正確調用析構函數。

④從實現上看,vbtl在構造函數調用後才建立,因而構造函數不可能成為虛函數

從實際含義上看,在調用構造函數時還不能確定對象的真實類型(因為子類會調父類的構造函數);而且構造函數的作用是提供初始化,在對象生命期只執行一次,不是對象的動態行為,也沒有太大的必要成為虛函數

⑤當一個構造函數被調用時,它做的首要的事情之一是初始化它的V P T R。因此,它只能知道它是「當前」類的,而完全忽視這個對象後面是否還有繼承者。當編譯器為這個構造函數產生代碼時,它是為這個類的構造函數產生代碼- -既不是為基類,也不是為它的派生類(因為類不知道誰繼承它)。

所以它使用的V P T R必須是對於這個類的V TA B L E。而且,只要它是最後的構造函數調用,那麼在這個對象的生命期內, V P T R將保持被初始化為指向這個V TA B L E, 但如果接著還有一個更晚派生的構造函數被調用,這個構造函數又將設置V P T R指向它的 V TA B L E,等.直到最後的構造函數結束。V P T R的狀態是由被最後調用的構造函數確定的。這就是為什麼構造函數調用是從基類到更加派生 類順序的另一個理由。

但是,當這一系列構造函數調用正發生時,每個構造函數都已經設置V P T R指向它自己的 V TA B L E。如果函數調用使用虛機制,它將只產生通過它自己的V TA B L E的調用,而不是最後的V TA B L E(所有構造函數被調用後才會有最後的V TA B L E)。
2、為什麼析構函數可以是虛函數
編譯器總是根據類型來調用類成員函數。但是一個派生類的指針可以安全地轉化為一個基類的指針。這樣刪除一個基類的指針的時候,C++不管這個指針指向一個基類對象還是一個派生類的對象,調用的都是基類的析構函數而不是派生類的。如果你依賴於派生類的析構函數的代碼來釋放資源,而沒有重載析構函數,那麼會有資源泄漏。
所以建議的方式是將析構函數聲明為虛函數。如果你使用MFC,並且以CObject或其派生類為基類,那麼MFC已經為你做了這件事情;CObject的析構函數是虛函數。一個函數一旦聲明為虛函數,那麼不管你是否加上virtual 修飾符,它在所有派生類中都成為虛函數。但是由於理解明確起見,建議的方式還是加上virtual 修飾符。
C++不把虛析構函數直接作為默認值的原因是虛函數表的開銷以及和c語言的類型的兼容性。有虛函數的對象總是在開始的位置包含一個隱含的虛函數表指針成員。如果是對於MFC類CPoint和CSize這樣的小型類,增加一個指針就增加了很多內存佔用,而且使得其內存表示和基類POINT和SIZE不一致。

『貳』 什麼函數不能聲明為虛函數

inline, static, constructor ,template 函數都不能 為虛函數,而析構函數可以。
為什麼呢:
inline: 編譯器替換; 而虛函數是為了解決運行期間綁定。
static:class 成員; 編譯期間就給class了。
constructor: 構造函數表示要生成一個class的object;假設是virtual的,那就說不知道這個實例化derived還是based的class。但是類型實例化必須在編譯期確定(否則編譯器不知道到底是什麼對象了)
template:模板實例是在 compile-time,virtual就意味著在run-time確定。這讓編譯器設計者為難了,這就是說虛函數表要指向各種版本的 template function 實例,代碼設計者confuse,編譯器設計者也覺得蠻煩。

『叄』 C++,虛函數,

如果派生類定義了一個與基類同名,但具有不同參數的函數,這個函數究竟有沒有被加入到派生類的虛函數表中?答案是yes。注意,不是虛函數就不來湊這個熱鬧了。

#include"pch.h"
#include<string>
#include<iostream>

usingstd::cout;
usingstd::endl;
classbrass
{
private:
inta;
public:
brass():a(5)
{
};
virtualvoidwith()
{
cout<<a<<endl;
}
};

classbrassplus:publicbrass
{
private:
intb;
public:
brassplus():b(6)
{
};
virtualvoidwith()
{
cout<<"1:"<<b<<endl;
}
virtualvoidwith(inti)
{
cout<<"2:"<<b<<endl;
}
};
intmain()
{
brass*a;
brassplusb;

a=(brass*)&b;
a->with();
b.with(1);
b.brass::with();
}

口說無憑,現在我們就上vs2017調試器,揭開虛函數表的面紗。首先用滑鼠在編輯器左欄下斷點,斷點下在brassplus b;一行,然後點擊「本地Windows調試器」進入調試模式。

『肆』 c語言編程,通過虛函數求派生類對象——某個圓柱體的體積。求助,怎麼編寫啊

#include<iostream>
#define PI 3.14159

class Circle
{
public:
float radius;
public:
Circle(float r)
{
radius = r;
};
virtual float area()
{
return PI * radius * radius;
};
virtual float volume()
{
return 0.0;
};
};

class Sphere : public Circle
{
public:
Sphere(float r)
{
radius = r;
};
virtual float area()
{
return 4 * PI * radius * radius;
};
virtual float volume()
{
return 4 * PI * radius * radius * radius / 3.0;
};
};

class Column : public Circle
{
public:
float height;
public:
Column(float r,float h)
{
radius = r;
height = h;
};
virtual float area()
{
return 2 * PI * radius * ( radius + height );
};
virtual float volume()
{
return PI * radius * radius * height;
};
};

int main()
{
Sphere s(20.0);
Column c(10.0,30.0);
cout << "s.area = "<<s.area()<<endl;
cout << "s.volume= "<<s.volume()<<endl;
cout << "c.area = "<<c.area()<<endl;
cout << "c.volume= "<<c.volume()<<endl;

return 0;
}

『伍』 C語言中void DigDisplay()是什麼意思

您好,很高興回答您的問題。
由於沒有看到其他的內容所以只能從結構上來說明相關含義。
void digdisplay()表示的是這個函數的定義,其中void表示函數不帶任何的返回值,digdisplay表示的是函數名,括弧中表示函數的參數,這里是空的,所以表示不帶任何的返回值。
以上就是我的回答,敬請指正。

『陸』 C++里比C里多出了繼承,多態,虛函數是啥回事求通俗解答…………謝謝!

C++是一門從C發展而來的編程語言,都是誕生於貝爾實驗室,因此是一脈相承的關系。
既然是發展,那就要多了些東西以適應更加復雜的編程要求咯。

就好比你最早買的U盤是usb1.0的,後來有了usb2.0,現在是usb3.0,顯然高版本的要比低版本的有更多的功能。同樣的道理C++就比C語言多了一些功能(嚴格地說叫語言特性)。多出來的這部分內容有2大塊,一個叫面向對象,也就是你說的封裝、繼承、多態;另外一部分叫泛型編程。

C++首先比C多出了類這個概念,你要先明白類是怎麼回事。

接下來說繼承,你老爸留下了一大筆遺產,你可以繼承並發揚光大,取其精華,去其糟粕。
同理,別人寫了一個C++的類A,你拿過來繼承一下,得到屬於你的類B,你的類B就有了別人的那部分功能,同時你再添的東西,於是類B把類A發揚光大了。
舉個現實中的例子,比如我現在用的搜狗輸入法具有換皮膚的功能,那麼這個功能就是一個類A,過了一段時間,新來了一個哥們,他想開發一個定時自動換皮膚的功能,他不需要把搜狗輸入法全部重新寫一遍,他只要把原來的功能A繼承過來,在裡面添加一個自動換皮膚的函數,於是就得到了新的類B。現在好了,原本要重新寫的程序,現在只要添點東西就OK,這不是節省了很多編程人員的精力了嗎。所以說繼承能夠讓你很快地用上別人的東西,同時添加自己的東西。

建議你看看 Head First 系列圖書,裡面有很多淺顯易懂的例子,你就會對面向對象編程有很好的了解了。當然,這一系列的書都價格不菲。

Head First深入淺出設計模式
http://book.douban.com/subject/1488876/
Head First深入淺出面向對象分析與設計(中文版)
http://book.douban.com/subject/3530721/

『柒』 C++中的virtual關鍵字是什麼怎麼用

virtual是定義C++中虛函數的關鍵字 。

1、virtual關鍵字的作用:

c++中的函數調用默認不適用動態綁定。要觸發動態綁定,必須滿足兩個條件:第一,指定為虛函數;第二,通過基類類型的引用或指針調用。由此可見,virtual主要功能是實現動態綁定。

2、virtual關鍵字的使用情況:

virtual可用來定義類函數和應用到虛繼承。

友元函數 構造函數 static靜態函數 不能用virtual關鍵字修飾;

普通成員函數 和析構函數 可以用virtual關鍵字修飾。

3、virtual關鍵字的效果:

class GrandFather //祖父類

{

public:

GrandFather() {} //構造函數

virtual void fun() //虛函數聲明定義

{

cout << "GrandFather call function!" << endl;

}

};

class Father : public GrandFather//父類,公有繼承祖父類

{

public:

Father() {} //構造函數

void fun() //fun函數聲明定義

{

cout << "Father call function!" << endl;

}

};

class Son : public Father //子類,公有繼承父類

{

public:

Son() {} //構造函數

void fun() //fun函數聲明定義

{

cout << "Son call function!" << endl;

}

};

void print(GrandFather* father) //輸出函數 ,祖父類形參

{

father->fun(); //調用fun函數

}

int _tmain(int argc, _TCHAR* argv[])

{

Father * pfather = new Son;//建立一個父類的指針讓它指向子類

pfather->fun();

GrandFather * pgfather = new Father;

print(pgfather); //祖父類指針變數

return 0; }

4、virtual的繼承性:

只要基函數定義了virtual,繼承類的該函數也就具有virtual屬性;即 GrandFather, Father ,Son同時定義virtual void fun()與GrandFather一個定義virtual void fun效果是一樣的。

(7)c語言虛函數擴展閱讀

vitual關鍵字的用途:

1、vitual基類

在多重繼承中,從派生類到基類存在多條路線時(多個繼承脈絡或者途徑),一個這種派生類的對象實例化將包含多個基類對象,浪費資源且調用混亂的現象出現。

因此引入了vitual baseclass,來在運行階段克服多個基類對象的產生。這個vitual是在運行階段保證對象唯一性的。

2.vitual函數

虛函數的出現,是由於基類的指針可以執行派生類,因此引出了不便,引入vitual函數,來告訴編譯器,出現這種情況時,在運行時動態鏈接進行處理。

3.vitual在純虛函數中使用

純虛函數完全是為了繼承體系的完整,也是集成vitual函數的作用而產生的。代表了編譯器階段對象的綁定,將調用選擇轉移到運行時動態綁定。

綜上:vitual關鍵的引入,可以理解為阻止編譯階段的靜態綁定,將綁定(虛函數)和約束工作(虛基類)轉移到運行時動態處理。


參考資料:網路——virtual

『捌』 C語言問題虛函數的問題

虛函數的作用是可以通過基類的指針或者引用調到派生類的這個函數。

你上面的代碼是演示虛函數的作用,不用去簡便他。

你可以把這個程序中的virtual全部刪除掉,然後再運行程序,觀察一下兩次結果的不一樣,估計你就能理解虛函數的作用了。

『玖』 C語言中什麼函數不能聲明為虛函數

c語言是面向過程的語言,c++是面向對象的

『拾』 在C++中,什麼是運算符重載什麼是虛函數

多態性是指用一個名字定義不同的函數,這函數執行不同但又類似的操作,從而實現「一個介面,多種方法」。

多態性的實現與靜態聯編、動態聯編有關。靜態聯編支持的多態性稱為編譯時的多態性,也稱靜態多態性,它是通過函數重載和運算符重載實現的。動態聯編支持的多態性稱為運行時的多態性,也稱動態多態性,它是通過繼承和虛函數實現的。

2.函數重載

函數重載的意義在於他能用同一個名字訪問一組相關的函數。

在類中普通成員函數和構造函數都可以重載,特別是構造函數的重載(他提供了多種初使化方式)給用戶更大的靈活性。在基類和派生類的函數重載有兩種情況:一種是參數有所差別的重載。另一種是參數沒有差別的重載,只是他們屬於不同的類。

可以用以下兩種方法來區分這兩種函數:用對象名加以區分;使用「類名::」加以區分。

3.運算符重載稱動態多態性,他是通過繼承和虛函數實現的。

運算符重載通過創建運算符函數[email protected]()來實現。運算符函數定義了重載的運算符將要進行的操作,這種操作通常作用在一個類上。這樣,在編譯時遇到名為[email protected]的運算符函數(@表示所要重載的運算符),就檢查傳遞給函數的參數的類型。

重載運算符與預定義運算符的使用方法完全相同,它不能改變原有運算符的參數個數(單目或雙目),也不能改變原有的優先順序的結合性。用戶不能定義新的運算符,只能從C++已有的運算符中選擇一個恰當的運算符重載。

3.1成員運算符函數

運算符函數可以定義為它將要操作的類的成員(稱為成員運算符函數),也可以定義為非類的成員,但是非成員的運算符函數大多是類的友元函數(稱為友元運算符函數)。

成員運算符函數在類中的聲明格式為:

class X{
//……
type [email protected](參數表);
};

其中type為函數的返回類型,@為所要重載的運算符符號,X是重載此運算符的類名,參數表中羅列的是該運算符所需要的操作數。

成員運算符函數定義的形式一般為:

type X::[email protected](參數表)

//函數體}

其符號的含義與聲明時相同。

在成員運算符函數的參數表中,若運算符是單目的,則參數表為空,此時當前對象作為運算符的一個操作數,通過this指針隱含地傳遞給函數的;若運算符是雙目的,則參數表中有一個操作數,它作為運算符的右操作參數,此時當前對象做為運算符的左操作數,它是this指針隱含地傳遞給函數的。總之成員運算符函數[email protected]所需要的一個操作數是由對象通過this指針隱含傳遞。

3.2友元運算符函數

在C++中可以把運算符函數定義成某個類的友元函數,稱為友元運算符函數。

友元運算符函數在類的內部聲明格式如下:

friend type [email protected](參數表)
定義友元運算符函數格式如下:
type [email protected](參數表)
{ //函數體
}

與成員運算符函數不同,友元運算符函數是不屬於任何類對象的,它沒有this指針。若重載的是雙目運算符,則參數表中有兩個操作數;若重載的是單目運算符,則參數表中只有一個操作數。 不能用友元函數重載的運算符是=、()、[]、-〉,其餘的運算符都可以使用友元函數來實現重載。

運算符函數調用形式如下表:

運算符函數調用形式

習慣形式 友元運算符函數調用形式 成員運算符函數調用形式

a+b operator(a,b) a.operator+(b)
-a operator-(a) a.operator-()
a++ operator++(a,0) a.operator++(0)

4.賦值運算符

繼承C語言,用戶自定義的類和結構都要能進行賦值運算。而數組名不能賦值,數組名實質上是一個常量指針。

對於任何類,C++提供了默認的賦值運算符。一般地,默認的賦值運算符重載是能夠勝任工作的。當類中有指針類型時,需要自定義賦值運算符函數。一般其函數體包含兩部分:

1)與析構函數類似,取消對象已經佔有的資源;

2)與構造函數類似,在其中分配新的資源。

類的賦值運算符重載「=」只能重載為成員函數,不能重載為友元函數。

重載後的運算符函數operator=()不能被繼承。

拷貝構造函數和賦值運算符重載的區別:聲明和定義方式不同;調用方式不同。

5.虛函數

虛函數是重載的另一種表現形式,允許虛函數調用與函數體之間的聯系在運行時才建立。
定義:虛函數就是在基類中被關鍵字virtual說明,並在派生類中重新定義的函數,在派生類中重新定義時,其函數原形包括返回類型,函數名,參數個數與參數類型的順序,都必須與基類中的原形必須相同。
構造函數不能是虛函數,但析構函數可以是虛函數。

虛函數與重載函數的關系:當普通的函數重載時,其函數的參數或參數類型必須有所不同,函數的返回類型也可不同;在派生類中,重新定義虛函數時要求函數名、返回類型、參數個數、參數的類型和順序與基類中的函數原形完全相同;若僅僅返回類型不同,其餘均相同,系統會給出錯誤信息。虛函數重載時若僅僅函數名相同,而參數的個數、類型或順序不同系統將它作為普遍函數重載,虛函數的特徵將會丟失。
多重繼承與虛函數:多重繼承可視為多個單繼承的組合。

6.純虛函數和抽象類

純虛函數:是一個在基類中說明的虛函數,他在該基類中沒有定義,但要求在它的派生類中定義自己的版本,或重新說明為純虛函數。

純虛函數的一般形式:virtual type func_name(參數表)=0(type是函數的返回類型,func_name是函數名)。

抽象類:一個類至少有一個純虛函數的類。抽象類提供了處理各種不同派生類的統一介面,將實現的責任交給了派生類。

另外,站長團上有產品團購,便宜有保證

熱點內容
瘋狂python 發布:2022-08-10 13:30:41 瀏覽:882
醫院基礎科室和人員怎麼配置 發布:2022-08-10 13:28:37 瀏覽:786
python中矩陣相乘 發布:2022-08-10 13:26:47 瀏覽:867
qtppython 發布:2022-08-10 13:25:43 瀏覽:368
胸12椎體壓縮性改變 發布:2022-08-10 13:21:15 瀏覽:353
存儲會戰 發布:2022-08-10 13:17:56 瀏覽:560
商城手機源碼 發布:2022-08-10 13:17:52 瀏覽:203
瀏覽器模擬手機訪問 發布:2022-08-10 13:16:45 瀏覽:475
安卓光遇伊甸的星河城堡在哪裡 發布:2022-08-10 13:16:40 瀏覽:769
爵跡臨界天下ftp 發布:2022-08-10 13:14:01 瀏覽:411