構造函數的需求
類内部的私有成員和保護成員在外界是不可以被通路的,這也正是類的封裝性的展現,是以在要為類的成員變量初始化時,這個任務就落在了類的成員函數。
class A
{
private:
int a;
int b;
public:
void init(int a,int b)
{
this->a=a;
this->b=b;
}
};
通過上述代碼,我們是可以通過調用init函數對類的成員變量進行指派,這意味着當我們建立一個對象,都要手動的調用init來初始化對象,這樣的類機制是不理想的,于是就想到在建立對象時,程式自動調用構造函數進行初始化。(析構函數同樣是程式自動調用的,它也是出于考慮到上述的原因)。另外考慮到類的唯一性和對象的多樣性,于是就打算用類名作為構造函數的函數名。(析構函數是使用~加類名作為函數名)。
構造函數的使用
#include<iostream>
using namespace std;
class A
{
private:
int a;
int b;
public:
A(int a, int b) //類的構造函數,函數名必須和類名相同。
{
this->a = a; //構造函數和普通成員函數一樣,它會有一個預設的參數,指向對象本身,就是this指針
this->b = b;
}
void print() //輸出函數
{
cout << a << endl;
cout << b << endl;
}
};
int main()
{
A a(10, 11);
a.print();
system("pause");
return 0;
}
輸出結果:
由上述的例子我們可以發現,構造函數是程式自己調用的,是以我們就完成了對a和b的初始化。
(構造函數是沒有傳回值的,也不允許有,但可以有無值的return。)
析構函數
析構函數也是特殊的函數,沒有傳回值,沒有參數,不能随意調用,沒有重載。隻是在類對象生命周期結束的時候自動調用。其實我們把析構函數稱為“逆構造函數”,在構造函數名前加上運算符“~”。析構函數的作用就是釋放對象的所使用的記憶體資源。析構函數的調用順序和構造函數的調用順序相反。
eg:
#include<iostream>
using namespace std;
class Student
{
public :
Student()
{
cout << "I'm a student" << endl;
}
~Student()
{
cout << "I'm a student 的析構" << endl;
}
};
class Teacher
{
public:
Teacher()
{
cout << "I'm a teacher" << endl;
}
~Teacher()
{
cout << "I'm a teacher 的析構" << endl;
}
};
class School
{
private:
/*
此處以其他類的對象作為成員變量
*/
Student s;
Teacher t;
public:
School()
{
cout << "I'm a school" << endl;
}
~School()
{
cout << "I'm a school 的析構" << endl;
}
};
int main()
{
School();
system("pause");
return 0;
}
如上述代碼所示,當一個類把其他類的對象作為成員變量時候,當該類建立對象自動調用該類的構造函數時,它也會同時自動調用其成員變量中類對象的構造函數,此時的構造函數調用順序為:先調用成員變量中的類對象的構造函數,按照聲明的先後順序調用,最後調用自己的構造函數。而析構函數的調用就和構造函數的調用順序相反。于是我們就可以得到如圖的輸出結果。
重載構造函數
首先我們解釋一下重載的含義就是函數或方法有相同的名稱,但是參數清單不同的情形。構造函數作為函數的一種,當然也可以被重載。下面給出函數重載的樣例,(string就是一個類,其中就是通過構造函數的重載完成對string對象多種初始化方式)
class A
{
private:
int a;
int b;
public:
/*
構造函數的重載,定義對象時,隻是通過參數的個數決定使用哪個構造函數
*/
A();
A(int a);
A(int a, int b);
};
重載構造函數為類提供了多種初始化方式,在定義對象時,程式根據所提供的參數個數來決定調用哪個構造函數。
預設的構造函數
(1):c++規定每個類必須要有一個構造函數,沒有構造函數,就無法建立對象
(2):如果沒定義構造函數,c++會自動的為提供一個預設的無參構造函數,但是該構造函數什麼也沒有幹,隻是拿來建立對象的。
(3):隻要為一個類定義了一個構造函數(無論是有參還是無參的),系統都不會再自動的為類提供構造函數,如果需要使用無參構造函數,那就需要自己定義一個無參構造函數。
(4):建立對象和定義普通變量是一樣的,在用預設的構造函數建立對象時,如果建立的是全局對象或靜态對象,則對象的位模式為全0,否則,對象是随機指派的。
類成員初始化問題
在一個類中,如果有使用者自定的類對象做為其成員(如前面講析構函數和構造函數調用順序),那麼構造函數是如何工作的了?
(1):定義該類時隻需要使用無參構造函數時
#include<iostream>
using namespace std;
class Student
{
public :
Student()
{
cout << "Student" << endl;
}
};
class School
{
private:
Student s; //定義了一個類成員
public:
School()
{
cout << "School" << endl; //在調用School的構造函數之前會先調用Student的構造函數
}
};
int main()
{
School s;
system("pause");
return 0;
}
從輸出結果可以看出,當我們建立了一個school對象時,它會調用構造函數來初始化其成員,其中它的一個類成員–Student s,也将調用它自己構造函數來初始化自己。
(2):當需要使用自定義構造函數(含參數)來初始化對象時
#include<iostream>
using namespace std;
class Student
{
private:
int a;
public :
Student(int a)
{
this->a = a;
cout << "Student a="<<this->a << endl;
}
};
class School
{
private:
Student s; //定義了一個類成員
public:
School(int a):s(a) //這是初始化成員變量的一種方式
{
cout << "School" << endl; //在調用School的構造函數之前會先調用Student的構造函數
}
};
int main()
{
School s(10);
system("pause");
return 0;
}
冒号文法來初始化成員變量的方式隻要是為了解決:類成員調用有參構造函數來初始化自己,成員變量為常量時,隻有通過這種方式才可以對其進行初始化,因為c++定義,常量是不可以被指派的。另外一個就是引用的變量的初始化,因為引用和常量是有共同的性質的。
class School
{
private:
const int age; //聲明了一個常量
int & i; //聲明了一個引用
public:
School(int & a) :age(10), i(a) //初始化引用和常量
{
}
};
#include<iostream>
using namespace std;
class A
{
public :
A(int a)
{
cout << "調用: " ;
cout << "a=" << a << endl;
}
};
void fun(int a)
{
static A example(a); //聲明靜态對象
A example1(a+1); //聲明非靜态對象
cout << "fun a=" << a<<endl;
}
int main()
{
fun(10);
fun(20);
system("pause");
return 0;
}