面向對象實驗 ——(二)類與對象

以前的博客:
C++ 構造函數+析構函數+函數參數的傳遞
C++ 模塊累積的理解

一、實驗目的及要求

內容:
(1)定義一個具體的類(如Point類),該類應包含構造函數、析構函數;
(2)定義一個組合類(如Line類),該組合類應該含有(1)中所定義的基本類內嵌對象,定義該組合類的構造及析構函數;
(3)利用所定義的組合類實現一定的具體功能。
要求:
1)掌握對象與類的基本概念、定義與使用;
(2)構造函數、析構函數的定義與使用;
(3)組合類的用法;
(4)記錄並分析實驗結果,撰寫實驗報告。

二、實驗過程及實驗結果分析

1.模塊的引入:
萬物皆模塊。對於模塊沒有一個明確的定義,可以把一個int變量看成是一個模塊,可以把一個for語句看成是一個模塊,也可以把一個函數看成是一個模塊。

2.複雜模塊類、對象:
在C++中,引入了類對象,可以把類對象看成是一個模塊。
(1)類的語法描述和抽象意義
從信息封裝和屏蔽角度來說,類模塊的成員分爲內部成員和對外接口;從數據操作角度來說,類的成員分爲數據成員和函數成員,後者即操作數據的成員。
(2)類的對外可見性
由模塊定義了對外展示哪些接口。
必須滿足:僅對外公開接口,但是模塊內部的所有元素都是相互可見的。
(3)存儲
在C語言裏,結構體也相當於一個模塊,先來回顧一下結構體的存儲:結構體所佔用的內存等於其各部分子元素變量的空間之和。
我們可以拓展到類和對象,對於數據成員來說,他的存儲和結構體的存儲類似;對於函數成員來說,類僅僅是給函數提供了一個作用域。
需要注意的是,兩成員的地位平等,均可以通過對象名,成員名調用。
(4)構造
一個模塊,由要素集合和關聯關係組成。對於一個int變量,我們可以直接進行賦值,賦值可以看成是已經構造好然後只需要組裝。
對於類和對象來說,由於構造規則複雜且不同,必須要有構造函數,構造函數的語法是與類同名。
構造函數可以分爲默認構造函數,無參構造函數,有參構造函數,拷貝構造函數,
對於這四類函數的區分可以結合函數重載進行理解。
(5)析構
C語言裏的局部變量在使用完畢後要釋放內存空間,而模塊使用後也要把申請的資源釋放掉,但是不能單純的釋放空間來達到限制模塊的作用域的目的,這就需要析構函數把模塊申請的資源釋放掉。
(6)類與對象的函數參數傳遞
先來回想一下函數調用的過程:1.記錄返回地址 2.爲被調函數的執行分配資源,創造運行條件3.將控制交給被調函數執行。
函數在進行參數傳遞時,有兩種方式,傳值調用和傳引用調用,這兩種方式的區別就在於第二步資源的分配。前者會爲實參數據在被調函數內部生成數據副本,後者是直接操作主調函數被的原始數據。
也就是說,對於類模塊的參數傳遞中的傳值方式,將會把主調函數中的實參轉化爲被調函數空間裏的形參。
對於普通的變量(相當於C語言中的局部變量),這一過程僅僅是申請空間後進行賦值,執行完畢後系統會釋放其內存,這也就保證了他的作用域。
而對於有自己的元素組成和構造方式的模塊來說,僅僅是單純的申請空間是無法生成新的模塊的,這就需要用拷貝構造函數用實參生成形參。在執行完畢後,也不能單純的釋放空間來達到限制模塊的作用域的目的,這就需要析構函數把模塊申請的資源釋放掉。
對於參數傳引用的方式,僅僅是對同一個數據模塊,在主調函數裏是一個名字,在被調函數裏是一個名字,相當於一個人的大名和小名~在這種方式下,無論是簡單的變量,還是複雜的類對象,處理和理解都是一樣的。
實驗代碼:

#include <iostream>
using namespace std;
class Point{///定義一個類對象相當於一個模塊
	private:
		int x,y;///類對象的數據成員,對外不可見
	public:///類對象的對外接口
    ///構造函數:與類同名,初始化對象的數據成員,使得小模塊形成完整模塊
		Point(int px,int py):x(px),y(py){
		  cout<<"普通構造函數生成"<<this<<endl;;
		  ShowPoint();
		}
    ///拷貝構造函數:完成完整模塊之間的複製
		Point(Point& p):x(p.x),y(p.y){
		  cout<<"拷貝構造函數生成"<<this<<endl;
		  ShowPoint();
		}
    ///顯示函數
		void ShowPoint(){ cout<<"("<<x<<","<<y<<")"<<this<<endl;}
    ///析構函數:在類前面加~,釋放模塊申請的資源
		~Point(){cout<<"析構函數調用"<<this<<endl;}
};
///void ShowPointInfo(Point p) 傳值調用:爲實參數據在被調函數內部生成數據副本
///void ShowPointInfo(Point& p) 傳引用調用:直接操作主調函數被的原始數據
void ShowPointInfo(Point p){
	cout<<"ShowPointInfo begin"<<endl;
	p.ShowPoint() ;
    cout<<"ShowPointInfo end"<<endl;
}
///傳值調用需要用拷貝構造函數用實參生成形參,在執行完畢後,需要析構函數把模塊申請的資源釋放掉
int main(){
	Point pa(3,4);
	ShowPointInfo(pa);
    cout<<endl;
	return 0;
}

運行結果:
在這裏插入圖片描述

過程分析:
首先調用構造函數對類對象的數據成員進行初始化,構造出類對象A
然後在函數傳值調用時調用拷貝構造函數將主調函數裏的實參轉爲被調函數裏的形參
這一過程是生成了一個新的類對象B
被調函數執行完畢後,類對象B被析構,返回主調函數
主調函數執行完畢,類對象A被析構,程序結束

3.組合類
即模塊累積。
通俗的解釋就是在構造時元素構成小模塊,小模塊構成大模塊。
析構的過程正好相反,大模塊析構後小模塊被暴露,小模塊再被析構
要注意:先構造的後析構
實驗代碼:

#include <bits/stdc++.h>
using namespace std;
class Point{///定義一個類對象相當於一個模塊
	private:
		int x,y;///類對象的數據成員,對外不可見
	public:///類對象的對外接口
    ///構造函數:與類同名,初始化對象的數據成員,使得小模塊形成完整模塊
		Point(int px,int py):x(px),y(py){
		  cout<<"普通構造函數生成"<<this<<endl;;
		  ShowPoint();
		}
    ///拷貝構造函數:完成完整模塊之間的複製
		Point(Point& p):x(p.x),y(p.y){
		  cout<<"拷貝構造函數生成"<<this<<endl;
		  ShowPoint();
		}
		void ShowPoint(){ cout<<"("<<x<<","<<y<<")"<<this<<endl;}
        int getx(){return x;}
        int gety(){return y;}
		~Point(){cout<<"析構函數調用"<<this<<endl;}
};
class Line{
    private:
        Point p1;
        Point p2;
        double len;
    public:
    ///pa->xp1 pb->xp2 拷貝構造函數
    ///xp1->p1 xp2->p2 拷貝構造函數
        /*Line(Point xp1,Point xp2):p1(xp1),p2(xp2){///構造函數
            cout<<"Line的構造函數"<<this<<endl;
        };
        Line(Line &L):p1(L.p1),p2(L.p2){
            cout<<"Line的拷貝構造函數"<<this<<endl;
        }*/
        Line(Point xp1,Point xp2);
        Line(Line &L);
        ~Line(){cout<<"Line的析構函數"<<this<<endl;}
        double getlen() {return len;}

};
///::預作用符 表示Line屬於Line類
///內聯函數:class內必須聲明才能使用
Line::Line(Point xp1,Point xp2):p1(xp1),p2(xp2){
     cout<<"Line的構造函數"<<this<<endl;
     double x=p1.getx()-p2.getx();
     double y=p1.gety()-p2.gety();
     len=sqrt(x*x+y*y);
}
///p1(L.p1)調用Point的拷貝構造函數
Line::Line(Line &L):p1(L.p1),p2(L.p2){
    cout<<"Line的拷貝構造函數"<<this<<endl;
}
void ShowPointInfo(Point p){
	cout<<"ShowPointInfo begin"<<endl;
	p.ShowPoint() ;
    cout<<"ShowPointInfo end"<<endl;
}
///加Line的拷貝構造函數L2(L1)
int main(){
	/*Point pa(3,4);
	ShowPointInfo(pa);
    cout<<endl;*/
    Point pa(3,4);
    Point pb(10,9);

    Line L1(pa,pb);
    cout<<"L2***********"<<endl;
    Line L2(L1);
    /*cout<<"L1 start point:";
    pa.ShowPoint();
    puts("");

    cout<<"L1 end point:";
    pb.ShowPoint();
    puts("");

    cout<<"The lengh of L1 is:"<<L1.getlen()<<endl;*/
	return 0;
}

以下結合this指針分析程序執行過程
運行結果:
普通構造函數生成0x6dfec8 ->根據參數構造pa
(3,4)0x6dfec8
普通構造函數生成0x6dfec0 ->根據參數構造pb
(10,9)0x6dfec0
拷貝構造函數生成0x6dfed0 ->根據pb生成xp2
(10,9)0x6dfed0
拷貝構造函數生成0x6dfed8 ->根據pa生成xp1
(3,4)0x6dfed8
拷貝構造函數生成0x6dfea8 ->根據xp1生成p1
(3,4)0x6dfea8
拷貝構造函數生成0x6dfeb0 ->根據xp2生成p2
(10,9)0x6dfeb0
Line的構造函數0x6dfea8 ->構造出L1
析構函數調用0x6dfed8 ->L1構造完成,xp1析構
析構函數調用0x6dfed0 ->xp2析構
L2*********** ->以下爲L2的拷貝構造
拷貝構造函數生成0x6dfe90 ->根據L1.p1構造L2的p1
(3,4)0x6dfe90
拷貝構造函數生成0x6dfe98 ->根據L1.p2構造L2的p2
(10,9)0x6dfe98
Line的拷貝構造函數0x6dfe90 ->L2拷貝構造成功
Line的析構函數0x6dfe90 ->L2析構
析構函數調用0x6dfe98 ->L2.p2析構
析構函數調用0x6dfe90 ->L2.p1析構
Line的析構函數0x6dfea8 ->L1析構
析構函數調用0x6dfeb0 ->L1.p2析構
析構函數調用0x6dfea8 ->L1.p1析構
析構函數調用0x6dfec0 ->pa析構
析構函數調用0x6dfec8 ->pb析構

終於完成了整理筆記的心願?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章