徐曉蓉+李永軍
摘要:類和對象是面向對象程序設計中非常重要的概念,也是初學者不容易理解的概念之一,該文用類比的方法清楚地解釋了類和對象的概念,并在內存層面對類和對象進行了深入的剖析,使學生能更加清晰地理解什么是類,什么是對象,以及類和對象在內存中的存儲形式。
關鍵詞:類和對象;數據和操作;方法;屬性和行為
中圖分類號:TP312 文獻標識碼:A 文章編號:1009-3044(2017)35-0106-03
A Thorough Analysis of Class and Object in C ++ by Analogy
XU Xiao-rong1, LI Yong-jun2
(1.College of Computer Science and Technology, Hunan University of Arts and Science, Changde 415000, China;2.School of Physics and Electronics, Henan University, Kaifeng 475004, China)
Abstract: Class and object are very important concepts in the Object-Oriented Programming, and they are very difficult to be understood by the beginners. In this paper, the concepts of class and object are explained clearly by analogy, and class and object are analyzed in memory, so that students can more easily understand what the class is , what the object is, and how class and object are stored in memory.
Key words:classes and objects; data and operation;methods; properties and behaviors
1 背景
類和對象是C++等面向對象程序設計中最重要的兩個基本概念,但就作者多年教授這門課的經驗看,學生大多因為有面向過程程序設計的基礎,最初都很難清楚地理解面向對象程序設計中的類和對象這兩個概念,從而碰到問題總是很容易想到面向過程而很難想到使用面向對象的方法來解決問題。本文通過類比法,對類和對象的定義進行了對比、分析和解釋,并在內存層面深入剖析了類和對象在內存中的存在形式,有助于初學者更快、更準確的理解類和對象的概念。
2 用類比法理解類和對象的定義
2.1 類的定義
數學上,所謂定義,即是對于一種事物的本質特征的確切而簡要的說明;比如,質數的定義:除了1和它本身以外不再有其他因數的大于1的自然數。
而在C++中,類的定義也是先找出類的特征,再給出類定義的描述,即必須首先對某類的若干對象進行分析,總結出該類對象(所關注)靜態的屬性(或數據)和動態的方法(或行為、或操作),即特征,最后再使用關鍵詞class將所有屬性和方法整合起來來形成類的定義。
比如:通過對很多個人特征的分析發現,對于人,通常關注的是姓名,性別,年齡等這些靜態的屬性,除此之外,人還有一些動態的行為,比如會思考、會說話、會微笑等等。當總結出了人的靜態和動態的特征之后,就可以定義什么是人!用 c++語言來描述出來就是如下程序段:
[例程1]
class Person
{private:
char name[20];
int age;
char sex;
public:
void think()
{ cout << "I am a person, and I can think!"< } void talk() { cout << "I am a person, and I can talk with you!" << endl; } void smile() { cout << "I am a person, and I like smiling!" << endl; } }; [例程1]中,class是C++中用來定義類的關鍵詞,而Person是定義的類的名字,類名可以由程序員自己定義。從[例程1]中可以看出,C++中動態行為是用函數來表達的,稱為成員函數,每一個成員函數都可以作為人與外界溝通交流的一個接口,所以,一般將成員函數定義成公有的(即public:)。而靜態的屬性,稱為數據成員,是用類型及屬性名來表示,一般是每個人所特有的,因此,一般將其定義為私有的(即private:)。當然,每個類中有幾個成員函數以及它們的類型、參數及具體實現的功能需要程序員自己去按實際需要來定義與實現,數據成員有幾個以及其名字、類型也是如此。 2.2 對象的定義 定義了類,相當于定義了一種新的數據類型,與系統的基本數據類型相似。也就是說,在[例程1]中定義Person類之后,就可以像以前使用的int、double、float等一樣來使用Person了,只不過,用系統的基本數據類型定義的變量,通常稱為變量,而用用戶自己定義的類型如Person定義的變量,通常稱之為對象而已,如:
int a; //a是變量,a中可以存放一個int型的數據。
Person p; //而p,則是對象,p中可以存放一個Person型的數據。
對象的定義格式為:類型 對象名;
2.3 類和對象的使用
類是抽象的,而對象則是具體的。類和對象的關系就好比人和某人張三的關系,人只是存在于我們的意念中,而張三這個人卻是實實在在存在與這個現實世界中的。張三具有人的所有屬性和行為。[例程1]中的Person類就是抽象的,要想使用Person類,必須定義Person類的對象,通過具體對象來使用類。如Person p; 定義了具體對象之后,就可以通過給對象發送指令來使用類,如p.smile();等。
3 類和對象的內存
3.1 類的內存
類是抽象的,因此,類的定義只是一種定義性說明,類定義中的數據成員本身是不占內存空間的,也就是說,類的內存空間僅僅是定義類的那段代碼所占據的內存空間。
3.2 對象的內存
對象是具體的,具有類的所有屬性和行為。所以,理論上來說,對對象而言,它的屬性和行為都要占據內存空間。
1) 普通類對象的內存
定義Person類對象,Person p1,p2;那么,p1和p2對象的內存示意圖如下圖1,圖2:
很顯然,對于同一個類的對象,每個對象中都有相同的成員函數,這將造成內存資源的極大浪費,因此,系統是將這些成員函數專門存儲在一個地方,每個對象的內存中只存儲相應的屬性,p1、p2的內存狀態如圖3所示。從圖3中可以看出,每個對象在內存中存儲的時候,只存儲屬于對象自己的數據成員,而沒有成員函數。對于不同對象如何使用存儲在同一個地方的相同成員函數在介紹this指針時再做解釋。
2) 派生類對象的內存
[例程2]在上面[例程1]的Person類的基礎上派生出如下的Student類。
class Student :public Person
{
int num;
char major[30];
public:
void printMessage()
{cout <<"num:"< }}; 從理論上講,派生類具有基類的所有屬性和行為。如果有Student s; s對象的內存狀態如圖4所示。由圖4可知,派生類對象的屬性具有兩類:基類的所有屬性、自己新增的屬性。但需注意,基類的屬性在派生類中是以無名對象(內嵌對象用橢圓表示,以下類同。)的形式存在的, 所以,s對象的內存示意如圖5所示。 3) 含虛基類的派生類對象的內存 當派生類的兩個基類有共同基類時,在派生類對象中就會有兩個共同基類的對象,如[例程3]中的Derive類的對象。 [例程3] class Base { int b; public: Base(int x=0) { b=x; cout<<"&b="<<&b< } }; class Base1: public Base { int b1; public: Base1(int x=0,int y=0):Base(x) { b1=y; } }; class Base2: public Base { int b2; public: Base2(int x=0,int y=0):Base(x) { b2=y; } }; class Derive:public Base1,public Base2 { int c; public: Derive(int x,int y,int z): Base1(100,y),Base2(200,z) { c=z; } }; void main() { Derive d(3,2,1); } 這時,在主函數中用Derive d(3,2,1)定義d對象之后,d的內存狀態如圖6所示。很顯然,對象d中有兩個共同基類Base類的對象,這樣,會造成二義性。由于在Base類的構造函數中加入了輸出b成員的地址,通過執行程序,由圖7可以很明顯的看到,輸出了兩次b的地址,說明,在d對象中,有兩個不同的Base類的對象。 為了消除這種二義性,c++中引入了虛基類,如[例程4]所示。 [例程4]該程序是在[例程3]的基礎上僅僅加了2個virtual和一個Base(300) class Base { int b; public: Base(int x=0) { b=x; cout<<"&b="<<&b< } }; class Base1: virtual public Base { int b1; public: Base1(int x=0,int y=0):Base(x)
{ b1=y; }
};
class Base2: virtual public Base
{
int b2;
public:
Base2(int x=0,int y=0):Base(x)
{ b2=y; }
};
class Derive:public Base1,public Base2
{
int c;
public:
Derive(int x,int y,int z): Base(300),Base1(100,y),Base2(200,z)
{ c=z; }
};
void main()
{
Derive d(3,2,1);
}
d對象的內存狀態如圖9所示,雖然,從內存看好似還是有兩個共同基類Base類的對象,但此時,這兩個對象的b的值已經是相同的,且是派生類Derive的構造函數提供的值,而且程序執行結果只輸出一個b的地址,如圖10所示。這說明,此時,在d對象中,已經只有一個共同基類Base類的對象了,從而消除了二義性,由此,我們也可以得到d對象的內存示意圖,如圖11所示。
4 結束語
初學者往往對類和對象的概念理解起來很不容易,本文通過類比法對類和對象的概念進行了分析解釋,并從內存層面對類和對象進行了深入的剖析,讓學生可以從概念到本質更好的理解類和對象的概念,通過實際教學和對學生的調查表明,通過此學習,學生確實可以更深一層的理解類和對象的概念。
參考文獻:
[1] 洪陽. C++中學習類的設計方法和途徑的探究[J]. 寶鋼科技, 2014,40(1):60-63.
[2] 陳維興, 林小茶. C++面向對象程序設計教程[M].北京: 清華大學出版社, 2009.
[3] 譚浩強. C++程序設計[M]. 3版.北京: 清華大學出版社, 2015.
[4] 本賈尼·斯特勞斯特魯普. C++程序設計:原理與實踐(基礎篇)[M]. 2版.北京: 機械工業出版社, 2017.