複習點:一、封裝、繼承和多態 二、重載、重寫(覆蓋)和隱藏 三、虛函數 四、malloc、free和new、delete的區別
封裝、繼承和多態
1、封裝
封裝是面向對象方法的重要原則,就是把對象的屬性和服務結合成一個獨立的系統單位,並且儘可能隱藏對象的內部細節。結合到c++中,這個獨立的系統單位就是類,類需要有高聚合和低耦合性,即內部的屬性和服務儘可能的聯繫緊密,而對外部只提供一些必要的接口。讓使用的人不必清除類內部是如何實現的。
2、繼承
繼承是面向對象技術能夠提高軟件開發的重要原因之一。通過繼承可以實現“代碼複用”,從而有效的較少的代碼的編寫量。
3、多態
多態性是指在一般的類中定義的屬性或行爲,被特殊類繼承後,可以具有不同的數據類型或表現出不同的行爲。
多態分爲:靜態多態和動態多態。其中靜態多態通過模板和重載實現,而動態多態則是通過虛函數機制實現。
靜態多態:即在編譯時,就決定了程序在執行時去運行具體那個函數。如:運算符重載,函數重載等。
動態多態:即在程序執行時,才能確定具體執行的時那個函數。如:虛函數。
重載、重寫(覆蓋)和隱藏
1、重載:是指在同一訪問區內(同一個類)被聲明的具有不同參數列表的同名函數,在函數調用時可以通過參數列表來確定調用的對象,重載不關心函數的返回值類型。即同一類中,同名不同參。
參數列表不同主要表現在以下幾個方面:
(1)參數的個數不同
(2)參數順序不同
(3)參數類型不同
這裏需要注意函數返回值類型的不同,並不是重載。但函數聲明爲const是可以被認定爲重載的。const對象只能調用cosnt方法,非const對象優先調用非const方法。
爲什麼在c語言中不支持函數重載?
在函數編譯時,會給每個函數設置一庫函數名。而在c語言中,這個庫函數名的生成僅僅只和函數名有關。假設一個函數的聲明爲
int fun(int n,char m);
在c中的庫函數名稱爲_fun,即僅僅之和函數名存在關係。
而在c++中的庫函數名稱爲_fun_int_char,即和函數名和參數列表有關(注意:和函數返回值無關)。
所以:在C語言中如何定義同名函數,編譯器是沒有辦法區分的,所以C語言中無法重載。
2、重寫(覆蓋):在派生類中重載對基類中的函數重新進行了實現,其函數名、返回值、參數列表都必須和基類中被重寫的函數一致,這樣就可以實現通過基類指針或引用來訪問派生類中重寫了的函數了。
3、隱藏:又被稱爲同名隱藏,指在派生類中實現了和基類的同名的方法,這樣派生類的對象就會訪問到派生類中實現的方法,而不是基類中的方法。
#include <iostream>
using namespace std;
class Test
{
public:
int fun(int n)
{
cout << "parent int n=" << n << endl;
return 0;
}
char fun(char n) //重載 同一個類的同名不同參的函數
{
cout << "parent char n=" << n << endl;
return 0;
}
virtual int fun1(int m)
{
cout << "parent int m=" << m << endl;
return 0;
}
};
class TestChild :public Test
{
public:
double fun(double n)//同名隱藏 派生類隱藏了基類的方法
{
cout << "child double n=" << n << endl;
return 0;
}
int fun1(int m) //重寫(覆蓋)派生類覆蓋了基類的方法
{
cout << "child int m=" << m << endl;
return 0;
}
};
int main()
{
Test test;
TestChild testChild;
test.fun(2);
test.fun('c');
testChild.fun(1.1);
Test *pTest = new Test();
pTest->fun1(15);
pTest = new TestChild();//通過父類指針使用基類的函數,可以寫virtual,也可以不寫
pTest->fun1(20);
return 0;
}
重載、重寫(覆蓋)和隱藏的區別?
(1)重載在同一個類中,重寫和隱藏是在基類和派生類中。
(2)重寫(覆蓋)派生類和基類的函數名,返回值、參數列表必須全部一致,而隱藏只需要和基類函數同名即可。
(3)重寫(覆蓋)基類必須爲虛函數。
虛函數(virtual)
上面提到動態多態主要通過虛函數機制實現,這裏介紹以下虛函數。和普通的函數聲明方式相同,只要在函數的返回值前加上virtual關鍵字,該函數就爲虛函數,即virtual 函數類型 函數名(形式參數)
虛函數的作用:允許通過基類的指針或引用來訪問基類和派生類的同名函數。
1、虛成員函數
具體來看一下代碼:
#include <iostream>
using namespace std;
class Teacher
{
public:
virtual void work()
{
cout << "上課" << endl;
}
};
class MathTeacher :public Teacher
{
public:
virtual void work()
{
cout << "數學課" << endl;
}
};
class ChinsesTeachar :public Teacher
{
public:
virtual void work()
{
cout << "語文課" << endl;
}
};
class EnglishTeachar :public Teacher
{
public:
virtual void work()
{
cout << "英語課" << endl;
}
};
int main()
{
cout << "上課啦!!!" << endl;
MathTeacher tea;
tea.Teacher::work();
tea.MathTeacher::work();//沒有虛函數只能這樣調用基類的函數
//定義一個指針數組
Teacher** teacher = new Teacher*[3]();
MathTeacher math;
EnglishTeachar english;
teacher[0] = new MathTeacher();
teacher[1] = new ChinsesTeachar();
teacher[2] = new EnglishTeachar();
for (int i = 0; i < 3; ++i)
{
teacher[i]->work();
}
return 0;
}
可以通過基類的指針區訪問派生的方法。
2、虛析構函數
在c++中不能聲明虛構造函數,但可以聲明虛析構函數。虛析構函數的作用就是爲了:防止因基類指針指向派生類的對象而造成的內存泄漏問題。
上面提到,通過基類指針可以指向派生類的對象,從而使用派生類方法。如果基類的析構函數不是虛函數的話,編譯器在執行時,就不會動態綁定,從而就是導致編譯器指調用了基類的析構函數,而沒有調用派生類的析構函數,這樣就會有潛在的內存泄漏的風險。而通過虛析構函數可以解決這一問題。
構造函數爲什麼不能是虛函數?
因爲虛函數的執行依賴於虛函數表,而虛函數表的初始化是在構造函數中完成,所以構造函數無法聲明爲虛函數。
爲什麼默認的析構函數不是虛函數?
虛函數的工作是基於虛函數表的,而虛函數表時需要消耗空間的。而在程序中又不一定又繼承,所以默認的析構函數不是虛函數。
總結:一個類的構造函數無法被聲明爲虛函數,而析構函數則可以聲明爲虛函數。如果程序中存在繼承那麼最好將析構函數設置爲虛析構函數。
3、純虛函數
//聲明格式:virtual 函數類型 函數名( 參數列表 ) =0; 可以沒有函數體的實現
virtual int fun(int n,int m)=0;
擁有純虛函數的類,爲抽象類。抽象類不能實例化對象。抽象類的子類會自動繼承該純虛函數,如果子類中任然沒有實現該方法,那麼該子類任然爲純虛函數。
malloc、free和new、delete的區別
1、malloc、free是c語言中的函數;new、delete是c++的運算符。
2、malloc在申請空間是地址的返回值是void*,需要用戶轉化;而new的返回值爲申請時的類型,不需要轉化。
3、malloc在調用時必須指定空間的大小;new在調用時會自動計算大小。
4、malloc只能申請基本類型和自定義的類型的空間,而new可以爲類申請空間。即malloc不能調用構造函數,而new可以。
5、在空間申請失敗時,malloc會返回NULL,而new會報錯。
6、malloc和free工作在堆(操作系統概念)上,而new和delete工作在自由存儲區(c++內存分區概念)上。
7、malloc和free時c語言中的函數不允許重載,而new和delete時c++的運算符所以允許重載。
這裏需要注意:c和c++中的內存分配還不是完全一樣的。
c中的內存分配:棧、堆、靜態存儲區、常量存儲區、程序代碼區
c++中的內存分配:棧、堆、自由存儲區、靜態存儲區、常量存儲區
在這裏需要記住:什麼是堆?即malloc和free操作的空間。什麼是自由存儲區?即new和delete操作的空間。
c++中的自由存儲區是一個抽象的概念的,它並不一定就是堆。new和delete的底層時藉助malloc和free實現的而malloc和free操作的對象就是堆,所以一般情況下自由存儲區就是堆,但new和delet屬於運算符,可以重載,這樣自由存儲就可以時別的空間了。