1. 程式人生 > >類的組合及初始化

類的組合及初始化

1.類的組合

類的組合描述的就是一個類內嵌其它類的物件作為成員的情況,它們之間的關係是一種包含與被包含的關係。
當建立類的物件時,如果這個類具有內嵌物件成員,那麼各個內嵌物件將首先被自動建立。因為部件物件是複雜物件的一部分。因此,在建立物件時既要對本類的基本型別資料成員進行初始化,又要對內嵌物件成員進行初始化。
組合類建構函式定義的一般形式是
類名::類名(形參表):內嵌物件1(形參表),內嵌物件2(形參表)…
{
初始化
}
1.呼叫內嵌物件的建構函式,呼叫順序按照內嵌物件在組合類的宣告中出現的次序。
2.執行本類建構函式的函式體。
如果宣告組合類的物件時沒有指定物件的初始值,則預設形式(無形參)的建構函式被呼叫,這時內嵌物件的預設形式建構函式也被呼叫。解構函式的呼叫執行順序與建構函式剛好相反。
當存在類的組合關係時,拷貝建構函式該如何編寫呢?對一個類,如果沒有編寫拷貝建構函式,編譯系統會在必要時自動生成一個預設的拷貝建構函式。若建立組合類的物件時呼叫預設拷貝建構函式,則編譯器將自動呼叫內嵌物件的拷貝建構函式。
如果要為組合類編寫拷貝建構函式,則需要為內嵌成員物件的拷貝建構函式傳遞引數。例如
,假設C類中包含B類的物件b作為成員,C類的拷貝建構函式形式如下:
C::C(C &c1):b(c1.b)
{}

2.拷貝建構函式

普通建構函式在物件建立時被呼叫,而拷貝建構函式在以下三種情況下會被呼叫:
1.當用類的一個物件去初始化該類的另一個物件時

int main(void)
{
  CPoint P1(0,0);
  CPoint P2(P1);//用物件P1去初始化物件P2,拷貝建構函式被呼叫
  return 0;
}

2.如果函式的形參是類的物件,呼叫函式時,進行形參和實參結合時。例如

void f(CPoint p)
{
  cout<<p.getx()<<endl;
}
void main()
{
  CPoint p(1,2);
  f(p)
;//函式的形參為類的物件,當呼叫函式時,拷貝建構函式被呼叫 }

3.如果函式的返回值是類的物件,函式執行完成返回呼叫者時。

CPoint g()
{
  CPoint p(1,2);
  return p;
}
void main()
{
  CPoint A;
  A=g();
}

函式g將p返回了主函式,但是A是g()的區域性物件,離開建立它的函式g以後就消亡了,不可能在返回主函式後繼續生存。所以在處理這種情況時編譯系統會在主函式中建立一個臨時的無名物件,該臨時物件的生存週期只在函式呼叫所處的表示式中,也就是A=g()中。執行語句“return p;”時,實際上是呼叫拷貝建構函式將p的值拷貝到臨時物件中。函式g執行結束時物件A消失,但臨時物件會存在於表示式A=g()中。計算完這個表示式,臨時物件的使命也就完成了,該臨時物件便自動消失。

3.形參帶預設引數

例子

#include <iostream>

using namespace std;

int sub(int x=8,int y=3){
    return x+y;
}

int main(){
    //freopen("D:\\input.in","r",stdin);
    //freopen("D:\\output.out","w",stdout);
    cout<<sub(20,15)<<endl;//35
    cout<<sub(10)<<endl;//13
    cout<<sub()<<endl;//11
    return 0;
}

1.若函式具有多個形參,則預設形參值必須自右向左連續地定義,並且在一個預設形參值的右邊不能有未指定預設值的引數。這是由於c++語言在函式呼叫時引數是自右向左入棧這一約定決定的。
eg:int f(int a, float b=5.0, char c=‘c’);
2.在呼叫一個函式時,如果省去了某個實參,則直到最右端的所有實參都得省去(當然,與其對應的形參要有預設值)。
eg:int f(int a, float b=5.0, char c=‘c’, int d=10); f(9,4.5) <=> f(9,4.5,‘c’,10).
3.預設形參值的說明必須出現在函式呼叫之前。而且,如果存在函式原型,則形參的預設值應在函式原型中指定;否則在函式定義中指定。另外,若函式原型中已給出了形參的預設值,則在函式定義中不得重複指定,即使所指定的預設值完全相同也不行。

#include <iostream>
 using namespace std;
 int sub(int x=8,int y=3);//函式原型中已經給出了形參的預設值
 int main(){
//freopen("D:\\input.in","r",stdin);
/freopen("D:\\output.out","w",stdout);
cout<<sub(20,15)<<endl;//35
cout<<sub(10)<<endl;//13
cout<<sub()<<endl;//11
return 0;
 }
 int sub(int x,int y)//在函式的定義中不能再指定
 {
    return x+y;
 }

4.在同一個作用域,一旦定義了預設形參值,就不能再定義它。
5.如果幾個函式說明出現在不同的作用域內,則允許分別為它們提供不同的預設形參值。

 #include <iostream>

using namespace std;

int sub(int x=8,int y=3);

int main(){
    //freopen("D:\\input.in","r",stdin);
    //freopen("D:\\output.out","w",stdout);
    int sub(int x=0,int y=0);
    cout<<sub()<<endl;
    return 0;
}

6.對形參預設值的指定可以是初始化表示式,甚至可以包含函式呼叫。

eg:int f(int a, float b=5.0, char c=‘c’, int d=sub(20,15));
7.在函式原型給出了形參的預設值時,形參名可以省略。
eg:int f(int, float=5.0, char=‘c’, int=sub(20,15));

4.結合拷貝函式,函式形參為預設值的情況來討論下類的組合下的物件初始化

先給出一個完整的例子:
定義一個Point類

#ifndef MYPOINT_H
#define MYPOINT_H


#include<iostream>
#include<cmath>
using namespace std;
class Point
{
public:
	Point(int xx,int yy=1) 
	{
		cout<<"這是點的帶參建構函式"<<endl;
	}
	Point()
	{
	   cout<<"這是點的無參建構函式"<<endl;
	}
	Point(Point &p)
	{
	   cout<<"這是點的拷貝建構函式"<<endl;
	}
private:
	int X,Y;

};
#endif

定義一個line類
line的標頭檔案

#include<iostream>
#include<cmath>
#include"Point.h"
using namespace std;


class Line
{
  public:
  Line(Point xp1,Point xp2);
  Line(Line &);
  Line();
  private:
  Point p1,p2;
};

line的CPP檔案

#include"stdafx.h"
#include"Line.h"
using namespace std;
Line::Line(Point xp1,Point xp2)//:p1(xp1),p2(xp2)
{
	cout<<"這是線的第一個建構函式"<<endl;
}
Line::Line()
{
	cout<<"這是線的第三個建構函式"<<endl;
}

主函式

#include "stdafx.h"
#include "stdlib.h"
#include"Line.h"
#include"Point.h"
int _tmain(int argc, _TCHAR* argv[])
{
	Point p1(0,0);
	Point p2(1,1);
	Line line(p1,p2);
	
	system("pause");
	return 0;
}

結果:
在這裡插入圖片描述
p1(0,0) ,p2(1,1)物件都是帶引數的,
line(p1,p2)中p1 p2屬於函式的形參是類的物件的情況,呼叫了拷貝建構函式,又line中的p1 p2沒有初始化,故呼叫了預設的建構函式,在成員變數初始化完成後,再動用line自身的建構函式,很明顯是帶參的建構函式
line的CPP檔案改為

#include"stdafx.h"
#include"Line.h"
using namespace std;
Line::Line(Point xp1,Point xp2):p1(xp1),p2(xp2)
{
	cout<<"這是線的第一個建構函式"<<endl;
}
Line::Line()
{
	cout<<"這是線的第三個建構函式"<<endl;
}

結果:
在這裡插入圖片描述
line中的p1 p2被顯示的初始化,故呼叫的都是拷貝函式

5.容易出錯的地方

帶引數預設值的建構函式Point(int xx=1,int yy=1);因為兩個形參都是帶預設值的,在引數都省略的情況下,和Point()會引起定義上的重複。Line(Point xp1,Point xp2)就無法判斷是哪個函式的初始化了。