1. 程式人生 > >為啥繼承時基類的析構一般宣告為虛擬函式

為啥繼承時基類的析構一般宣告為虛擬函式

1、為啥繼承時基類的解構函式宣告為虛擬函式?

文字描述太抽象了,直接用程式碼給出答案。

(1)第一段程式碼:

#include<iostream>
using  namespace std ;

class Base
{
public:
	Base()
	{
		
		cout << "基類的建構函式被呼叫了" << endl;
		
	}
	 ~Base()
	{
		
		cout << "基類的解構函式被執行了" <<endl;
	}

};

class Derived :public Base
{
public:
	Derived(char *str = "")
	{
		if (str == NULL)
		{
			_Dstr = new char[1];
			*_Dstr = '\0';
		}
		else
		{
			_Dstr = new char [strlen(str) + 1];
			strcpy(_Dstr, str);
		}
		cout << "派生類的建構函式被呼叫了" << endl;

	}
	~Derived()
	{
		if (_Dstr != NULL)
		{
			delete [] _Dstr;
			_Dstr = NULL;
		}
		cout << "派生類的解構函式被執行了" <<endl;
	}
private:
	char *_Dstr;
};


void test()
{
	Base B1;
	Base & b = B1;   //基類的引用或指標指向基類物件。
}


int main()
{
	test();

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}


(2)第二段程式碼

#include<iostream>
using  namespace std ;

class Base
{
public:
	Base()
	{
		
		cout << "基類的建構函式被呼叫了" << endl;
		
	}
	 ~Base()
	{
		
		cout << "基類的解構函式被執行了" <<endl;
	}

};

class Derived :public Base
{
public:
	Derived(char *str = "")
	{
		if (str == NULL)
		{
			_Dstr = new char[1];
			*_Dstr = '\0';
		}
		else
		{
			_Dstr = new char [strlen(str) + 1];
			strcpy(_Dstr, str);
		}
		cout << "派生類的建構函式被呼叫了" << endl;

	}
	~Derived()
	{
		if (_Dstr != NULL)
		{
			delete [] _Dstr;
			_Dstr = NULL;
		}
		cout << "派生類的解構函式被執行了" <<endl;
	}
private:
	char *_Dstr;
};


void test()
{

	Base * b = new Derived("str");   //基類的引用或指標指向派生類物件。
	delete b;
}


int main()
{
	test();

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}


//分析答案發現析構時,只調用了基類的解構函式,沒有呼叫派生類的解構函式,導致派生類記憶體洩露,

(3)第三段程式碼

#include<iostream>
using  namespace std ;

class Base
{
public:
	Base()
	{
		
		cout << "基類的建構函式被呼叫了" << endl;
		
	}
	 virtual ~Base()  //將基類的解構函式宣告為虛擬函式。 
	{
		
		cout << "基類的解構函式被執行了" <<endl;
	}

};

class Derived :public Base
{
public:
	Derived(char *str = "")
	{
		if (str == NULL)
		{
			_Dstr = new char[1];
			*_Dstr = '\0';
		}
		else
		{
			_Dstr = new char [strlen(str) + 1];
			strcpy(_Dstr, str);
		}
		cout << "派生類的建構函式被呼叫了" << endl;

	}
	~Derived()
	{
		if (_Dstr != NULL)
		{
			delete [] _Dstr;
			_Dstr = NULL;
		}
		cout << "派生類的解構函式被執行了" <<endl;
	}
private:
	char *_Dstr;
};


void test()
{

	Base * b = new Derived("str");   //基類的引用或指標指向派生類物件。
	delete b;
}


int main()
{
	test();

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}


//答案分析:析構時,先呼叫了派生類的解構函式,再呼叫基類的解構函式,不存在記憶體洩露。反過來看原始碼只是發現只是將基類的解構函式宣告為虛擬函式。

結論: 繼承時,如果派生類有資源需要自己手動釋放,最好將基類的解構函式宣告為虛擬函式,這樣我們可以通過基類的指標或引用去釋放子類的資源。防止記憶體洩露。

2.當我們想要繼承一個抽象類的時候,但是還不能確定抽象類的那些成員是純虛擬函式,我們可以將抽象類的解構函式宣告為純虛擬函式。

(1)第一段程式碼

#include<iostream>
using  namespace std ;

class Abstract
{
public:
	virtual ~Abstract() = 0;
};

class Derived: public Abstract
{
public:
	Derived()
	{
		cout << "派生類的建構函式被執行了" <<endl;
	}
	~Derived()
	{
		cout << "派生類的解構函式被執行了" << endl;
	}
};

void test()
{
	Derived d;
}

int main()
{
	test();

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}

如果我們單純的直接這麼寫,我們發現編譯器報錯,報下列的錯誤:


抽象類的解構函式.obj : error LNK2019: 無法解析的外部符號 "public: virtual __thiscall Abstract::~Abstract(void)" ([email protected]@[email protected]),該符號在函式 [email protected]@[email protected]

通過分析:我們發現定義派生類物件時,會先呼叫抽象類的建構函式,抽象類的建構函式沒有顯示的給出,編譯器合成一個預設的建構函式,再呼叫派生類的建構函式,析構時,先呼叫派生類的解構函式,最後呼叫抽象類的解構函式,但是抽象類的解構函式已經顯示的聲明瞭,因此會呼叫程式設計師自己寫的,但是這個解構函式沒有函式體,所以編譯器報錯,說無法解析的外部符號,

解決方案:給抽象類的解構函式定義。看下面的程式碼:

(2)第二段程式碼:

#include<iostream>
using  namespace std ;

class Abstract
{
public:
	virtual ~Abstract() = 0;
	
};

 Abstract:: ~Abstract()
{
	cout << "抽象類的解構函式被執行了" <<endl;
};

class Derived: public Abstract
{
public:
	Derived()
	{
		cout << "派生類的建構函式被執行了" <<endl;
	}
	~Derived()
	{
		cout << "派生類的解構函式被執行了" << endl;
	}
};

void test()
{
	Derived d;
}

int main()
{
	test();

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}


相關推薦

為啥繼承一般宣告虛擬函式

1、為啥繼承時基類的解構函式宣告為虛擬函式? 文字描述太抽象了,直接用程式碼給出答案。 (1)第一段程式碼: #include<iostream> using namespace std ; class Base { public: Base() {

條款7:多態函數聲明virtual

是你 避免 class 基類 多態 聲明 ase delet 必須 基類指針指向子類對象。 子類對象必須位於堆。因此為了避免泄漏內存資源,當指針不使用時,delete掉每一個對象非常重要。但是如果基類的析構函數不聲明為virtual。那麽指向子類對象的指針delete時,析

繼承命名衝突的解決方案

 眾所周知,C++與其他語言(如C#,JAVA)一個很大的不同就是C++支援從多個類繼承。但是多繼承經常遇到這樣一種情況,如果有兩個或多個基類有相同名字和標記的方法,繼承類該怎麼去實現。如有2個基類,程式碼如下: class CBaseA { public: virtua

(轉載)(C++)淺談多型函式宣告虛擬函式

主要內容: 1、C++類繼承中的建構函式和解構函式 2、C++多型性中的靜態繫結和動態繫結 3、C++多型性中解構函式宣告為虛擬函式 1、C++類繼承中的建構函式和解構函式 在C++的類繼承中, 建立物件時,首先呼叫基類的建構函式,然後在呼叫下一

(C++)淺談多型函式宣告虛擬函式

主要內容: 1、C++類繼承中的建構函式和解構函式 2、C++多型性中的靜態繫結和動態繫結 3、C++多型性中解構函式宣告為虛擬函式 1、C++類繼承中的建構函式和解構函式 在C++的類繼承中, 建立物件時,首先呼叫基類的建構函式,然後在呼叫下一個派生類的建構函式,依次類推; 析構物件時,其

C++函式宣告虛擬函式

先來看幾段程式例子:1. 將基類解構函式宣告為虛擬函式#include <iostream

構造、期間被調虛擬函式發生的慘案,長教訓!

最近有個問題出現長達一個月,經過兩次修改未能解決,大致場景如下: 一個多型物件Children被註冊回撥(m_observer物件位於基類Base中),正好在解構函式裡面回撥,導致crash。 class Base { // ... protected: std::shared_ptr&l

函式為什麼不能宣告虛擬函式?解函式為什麼要宣告虛擬函式

多型中的虛擬函式表是在執行時建立的還是編譯時建立的? 答:虛擬函式表在編譯的時候就確定了,而類物件的虛擬函式指標vptr是在執行階段確定的,這是實現多型的關鍵 (類的函式的呼叫並不是在編譯時就確定的,而是在執行時才確定的,由於編寫程式碼的時候並不能確定被呼叫的是基類的函式還是哪個派生類的函式,所以宣告為虛

建構函式和解函式能否宣告虛擬函式

建構函式不能宣告為虛擬函式,解構函式可以宣告為虛擬函式,而且有時是必須宣告為虛擬函式。 不建議在建構函式和解構函式裡面呼叫虛擬函式。 建構函式不能宣告為虛擬函式的原因是: 解釋一:所謂虛擬函式就是多型情況下只執行一個。而從繼承的概念來講,總是要先構造父類物件,然後才

為什麼建構函式不能宣告虛擬函式,解函式可以

1. 如果我們定義了一個建構函式,編譯器就不會再為我們生成預設構造函數了。2. 編譯器生成的解構函式是非虛的,除非是一個子類,其父類有個虛析構,此時的函式虛特性來自父類。3. 有虛擬函式的類,幾乎可以確定要有個虛解構函式。4. 如果一個類不可能是基類就不要申明解構函式為虛擬函式,虛擬函式是要耗費空間的。5.

關於繼承的構造與調用分析

fff 調用父類 派生類的構造函數 臨時 臨時對象 構造函數 back 基類 原因分析   總體結論:派生類的構造函數在創建對象時調用,調用順序如下:        1.調用虛基類的構造函數(若有多個虛基類,調用順序為繼承的順序。);        2.調用基類的構造函

C++-函數什麽要加virtual虛函數(轉)

nbsp 分享圖片 spa 防止 too bsp 測試 ++i tails 知識背景 要弄明白這個問題,首先要了解下C++中的動態綁定。 關於動態綁定的講解,請參閱: C++中的動態類型與動態綁定、虛函數、多態實現 正題

繼承的構造和函數調用順序

gin pre ont 關於 類名 image isp .com 派生 聲明多繼承的方法 多繼承與單繼承的區別僅在於它們基類的個數。在定義多繼承的派生類時,要指出它們所有基類名以及繼承方式。 聲明形式如下: class 派生類名:繼承方式1 基類名1,繼承方式2,基類名2,

3.8 C++繼承機制下的函數

right clas const source color ner fix wrap 派生類的構造函數 參考:http://www.weixueyuan.net/view/6365.html 總結:   構造函數的執行順序是按照繼承順序自頂向下的,從基類到派生類,而析構函數

C/C++的解函式為什麼必須定義虛擬函式

C/C++基類的解構函式為什麼必須定義為虛擬函式?   為什麼基類的解構函式是虛擬函式? 在實現多型時,當用基類操作派生類,在析構時防止只析構基類而不析構派生類的狀況發生。 (1)第一種情況:沒有多型,建立派生類物件,基類的解構函式不是虛擬函式 #include<

C# 中的靜態欄位始終繼承

我們試想一下現在有一個類Parent,它有一個static的int型別欄位number,然後如果類Parent有三個子類Child01、Child02和Child03,那麼改變Parent.number的值的話,Child01.number、Child02.number和Child03.number的值也會被

C++ 的解函式為什麼需要定義虛擬函式

主要是因為當通過父類的指標或引用指向子類的物件時,刪除子類時父類的解構函式不能被呼叫造成記憶體洩露。 1.當基類的引用指向基類的物件 #include<iostream> #include<cstring> #include<cstdlib> using

servlet、filter、listener繼承和獲得作用域的方式

一、servlet:  1、servlet屬於j2ee的元件,構建servlet的web project不需要匯入專案框架jar包  2、servlet的體系結構:   在j2ee API中,提供給servlet的支援介面和基類都位於javax.servlet.*和java

函式虛擬函式

    基類解構函式為什麼要宣告為虛擬函式?     這個問題是C++理解虛擬函式和動態繫結的一個基礎,也是面試常考的一個點。雖然幾乎每本書中都會提及這個點,但是很少有把其具體闡明。本文通過具體例項+理論闡述儘量清晰解釋這個問題。

C++-繼承與派生的關係

成員函式的重定義和名字隱藏 基類的資料成員和成員函式在派生類中都有一份拷貝,派生類能夠直接訪問從基類繼承而來的public和protected成員,且只能夠通過這兩類成員訪問從基類繼承而來的private成員。 派生類不僅可以新增基類沒有的新成員,而且可以對