国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

c++面向對象的編程入門篇--類構造函數與析構函數

2019-11-17 05:06:51
字體:
來源:轉載
供稿:網友
請注重,這一節內容是c++的重點,要非凡注重!


我們先說一下什么是構造函數?

上一個教程我們簡單說了關于類的一些基本內容,對于類對象成員的初始化我們始終是建立成員函數然后手工調用該函數對成員進行賦值的,那么在c++中對于類來說有沒有更方便的方式能夠在對象創建的時候就自動初始化成員變量呢,這一點對操作保護成員是至關重要的,答案是肯定的關于c++類成員的初始化,有專門的構造函數來進行自動操作而無需要手工調用,在正式講解之前先看看c++對構造函數的一個基本定義。

1.C++規定,每個類必須有默認的構造函數,沒有構造函數就不能創建對象。

2.若沒有提供任何構造函數,那么c++提供自動提供一個默認的構造函數,該默認構造函數是一個沒有參數的構造函數,它僅僅負責創建對象而不做任何賦值操作。

3.只要類中提供了任意一個構造函數,那么c++就不在自動提供默認構造函數。

4.類對象的定義和變量的定義類似,使用默認構造函數創建對象的時候,假如創建的是靜態或者是全局對象,則對象的位模式全部為0,否則將會是隨即的。



我們來看下面的代碼:


//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必聞名出處和作者

#include <iostream>
using namespace std;
class Student
{
public:
Student()//無參數構造函數
{
number = 1;
score = 100;
}
void show();

PRotected:
int number;
int score;

};

void Student::show()
{
cout<<number<<endl<<score<<endl;
}

void main()
{
Student a;
a.show();
cin.get();
}


在類中的定義的和類名相同,并且沒有任何返回類型的Student()就是構造函數,這是一個無參數的構造函數,他在對象創建的時候自動調用,假如去掉Student()函數體內的代碼那么它和c++的默認提供的構造函數等價的。
更多文章 更多內容請看C/C++技術學堂專題,或 構造函數可以帶任意多個的形式參數,這一點和普通函數的特性是一樣的!

下面我們來看一個帶參數的構造函數是如何進行對象的始化操作的。

代碼如下:


//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必聞名出處和作者

#include <iostream>
using namespace std;
class Teacher
{
public:
Teacher(char *input_name)//有參數的構造函數
{
name=new char[10];
//name=input_name;//這樣賦值是錯誤的
strcpy(name,input_name);
}
void show();

protected:
char *name;

};

void Teacher::show()
{
cout<<name<<endl;
}

void main()
{
//Teacher a;//這里是錯誤的,因為沒有無參數的構造函數
Teacher a("test");
a.show();
cin.get();
}


我們創建了一個帶有字符指針的帶有形參的Teacher(char *input_name)的構造函數,調用它創建對象的使用類名加對象名稱加擴號和擴號內參數的方式調用,這和調用函數有點類似,但意義也有所不同,因為構造函數是為創建對象而設立的,這里的意義不單純是調用函數,而是創建一個類對象。

一旦類中有了一個帶參數的構造函數而又沒無參數構造函數的時候系統將無法創建不帶參數的對象,所以上面的代碼

Teacher a;

就是錯誤的?。?!


這里還有一處也要注重:

//name=input_name;//這樣賦值是錯誤的

因為name指是指向內存堆區的,假如使用name=input_name;會造成指針指向改變不是指向堆區而是指向棧區,導致在后面調用析構函數delete釋放堆空間出錯!(析構函數的內容我們后面將要介紹)

假如需要調用能夠執行就需要再添加一個沒有參數的構造函數

對上面的代碼改造如下:


//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必聞名出處和作者

#include <iostream>
using namespace std;
class Teacher
{
public:
Teacher(char *input_name)
{
name=new char[10];
//name=input_name;//這樣賦值是錯誤的
strcpy(name,input_name);
}
Teacher()//無參數構造函數,進行函數重載
{

}
void show();

protected:
char *name;

};

void Teacher::show()
{
cout<<name<<endl;
}

void main()
{
Teacher test;
Teacher a("test");
a.show();
cin.get();
}
創建一個無闡述的同名的Teacher()無參數函數,一重載方式區分調用,由于構造函數和普通函數一樣具有重載特性所以編寫程序的人可以給一個類添加任意多個構造函數,來使用不同的參數來進行初始話對象! 更多文章 更多內容請看C/C++技術學堂專題,或 現在我們來說一下,一個類對象是另外一類的數據成員的情況,假如有點覺得饒人那么可以簡單理解成:類成員的定義可以相互嵌套定義,一個類的成員可以用另一個類進行定義聲明。


c++規定假如一個類對象是另外一類的數據成員,那么在創建對象的時候系統將自動調用那個類的構造函數。

下面我們看一個例子。

代碼如下:


//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必聞名出處和作者

#include <iostream>
using namespace std;
class Teacher
{
public:
Teacher()
{
Director = new char[10];
strcpy(director,"王大力");
}
char *show();
protected:
char *director;
};
char *Teacher::show()
{
return director;
}
class Student
{
public:
Student()
{
number = 1;
score = 100;
}
void show();

protected:
int number;
int score;
Teacher teacher;//這個類的成員teacher是用Teacher類進行創建并初始化的

};

void Student::show()
{
cout<<teacher.show()<<endl<<number<<endl<<score<<endl;
}

void main()
{
Student a;
a.show();
Student b[5];
for(int i=0; i<sizeof(b)/sizeof(Student); i++)
{
b[i].show();
}
cin.get();
}


上面代碼中的Student類成員中teacher成員是的定義是用類Teacher進行定義創建的,那么系統碰到創建代碼的時候就會自動調用Teacher類中的Teacher()構造函數對對象進行初始化工作!

這個例子說明類的分工很明確,只有碰到自己的對象的創建的時候才自己調用自己的構造函數! 更多文章 更多內容請看C/C++技術學堂專題,或 一個類可能需要在構造函數內動態分配資源,那么這些動態開辟的資源就需要在對象不復存在之前被銷毀掉,那么c++類的析構函數就提供了這個方便。


析構函數的定義:析構函數也是非凡的類成員函數,它沒有返回類型,沒有參數,不能隨意調用,也沒有重載,只有在類對象的生命期結束的時候,由系統自動調用。

析構函數與構造函數最主要大不同就是在于調用期不同,構造函數可以有參數可以重載!

我們前面例子中的Teacher類中就使用new操作符進行了動態堆內存的開辟,由于上面的代碼缺少析構函數,所以在程序結束后,動態開辟的內存空間并沒有隨著程序的結束而小時,假如沒有析構函數在程序結束的時候逐一清除被占用的動態堆空間那么就會造成內存泄露,使系統內存不斷減少系統效率將大大降低!

那么我們將如何編寫類的析構函數呢?

析構函數可以的特性是在程序結束的時候逐一調用,那么正好與構造函數的情況是相反,屬于互逆特性,所以定義析構函數因使用"~"符號(邏輯非運算符),表示它為膩構造函數,加上類名稱來定義。

看如下代碼:


//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必聞名出處和作者

#include <iostream>
#include <string>
using namespace std;
class Teacher
{
public:
Teacher()
{
director = new char[10];
strcpy(director,"王大力");
//director = new string;
// *director="王大力";//string情況賦值
}
~Teacher()
{
cout<<"釋放堆區director內存空間1次";
delete[] director;
cin.get();
}
char *show();
protected:
char *director;
//string *director;
};
char *Teacher::show()
{
return director;
}
class Student
{
public:
Student()
{
number = 1;
score = 100;
}
void show();

protected:
int number;
int score;
Teacher teacher;

};

void Student::show()
{
cout<<teacher.show()<<endl<<number<<endl<<score<<endl;
}
void main()
{
Student a;
a.show();
Student b[5];
for(int i=0; i<sizeof(b)/sizeof(Student); i++)
{
b[i].show();
}
cin.get();
}


上面的代碼中我們為Teacher類添加了一個名為~Teacher()的析構函數用于清空堆內存。

建議大家編譯運行代碼觀察調用情況,程序將在結束前也就是對象生命周期結束的時候自動調用~Teacher()

~Teache()中的delete[] director;就是清除堆內存的代碼,這與我們前面一開始提到的。

name=input_name;//這樣賦值是錯誤的

有直接的關系,因為delete操作符只能清空堆空間而不能清楚桟空間,假如強行清除棧空間內存的話將導致程序崩潰?。? 更多文章 更多內容請看C/C++技術學堂專題,或 前面我們已經簡單的說了類的構造函數和析構函數,我們知道一個類的成員可以是另外一個類的對象,構造函數答應帶參數,那么我們可能會想到上面的程序我們可以在類中把Student類中的teacher成員用帶參數的形式調用Student類的構造函數,不必要再在Teacher類中進行操作,
由于這一點構想我們把程序修改成如下形式:


//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必聞名出處和作者

#include <iostream>
#include <string>

using namespace std;
class Teacher
{
public:
Teacher(char *temp)
{
director = new char[10];
strcpy(director,temp);
}
~Teacher()
{
cout<<"釋放堆區director內存空間1次";
delete[] director;
cin.get();
}
char *show();
protected:
char *director;
};
char *Teacher::show()
{
return director;
}
class Student
{
public:
Student()
{
number = 1;
score = 100;
}
void show();

protected:
int number;
int score;
Teacher teacher("王大力");//錯誤,一個類的成員假如是另外一個類的對象的話,不能在類中使用帶參數的構造函數進行初始化

};

void Student::show()
{
cout<<teacher.show()<<endl<<number<<endl<<score<<endl;
}
void main()
{
Student a;
a.show();
Student b[5];
for(int i=0; i<sizeof(b)/sizeof(Student); i++)
{
b[i].show();
}
cin.get();
}



可是很遺憾,程序不能夠被編譯成功,為什么呢?

因為:類是一個抽象的概念,并不是一個實體,并不能包含屬性值(這里來說也就是構造函數的參數了),只有對象才占有一定的內存空間,含有明確的屬性值!


這一個問題是類成員初始化比較尷尬的一個問題,是不是就沒有辦法解決了呢?呵呵。。。。。。

c++為了解決此問題,有一個很獨特的方法,下一小節我們將介紹。 更多文章 更多內容請看C/C++技術學堂專題,或 對于上面的那個"尷尬"問題,我們可以在構造函數頭的后面加上:號并指定調用哪那個類成員的構造函數來解決!

教程寫到這里的時候對比了很多書籍,發現幾乎所有的書都把這一章節叫做構造類成員,筆者在此覺得有所不妥,因為從讀音上輕易混淆概念,所以把這一小節的名稱改為構造類的成員比較合適!

代碼如下:


//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必聞名出處和作者

#include <iostream>
using namespace std;
class Teacher
{
public:
Teacher(char *temp)
{
director = new char[10];
strcpy(director,temp);
}
~Teacher()
{
cout<<"釋放堆區director內存空間1次";
delete[] director;
cin.get();
}
char *show();
protected:
char *director;
};
char *Teacher::show()
{
return director;
}
class Student
{
public:
Student(char *temp):teacher(temp)
{
number = 1;
score = 100;
}
void show();

protected:
int number;
int score;
Teacher teacher;

};

void Student::show()
{
cout<<teacher.show()<<endl<<number<<endl<<score<<endl;
}
void main()
{
Student a("王大力");
a.show();
//Student b[5]("王大力"); //這里這么用是不對的,數組不能夠使用帶參數的構造函數,以后我們將具體介紹vector類型
// for(int i=0; i<sizeof(b)/sizeof(Student); i++)
//{
// b[i].show();
//}

cin.get();
}




大家可以發現最明顯的改變在這里

Student(char *temp):teacher(temp)

冒號后的teacher就是告訴調用Student類的構造函數的時候把參數傳遞給成員teacher的Teacher類的構造函數,這樣一來我們就成功的在類體外對teacher成員進行了初始化,既方便也高效,這種冒號后指定調用某成員構造函數的方式,可以同時制定多個成員,這一特性使用逗號方式,例如:

Student(char *temp):teacher(temp),abc(temp),def(temp)


由冒號后可指定調用哪那個類成員的構造函數的特性,使得我們可以給類的常量和引用成員進行初始化成為可能。

我們修改上面的程序,得到如下代碼:


//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必聞名出處和作者

#include <iostream>
#include <string>
using namespace std;
class Teacher
{
public:
Teacher(char *temp)
{
director = new char[10];
strcpy(director,temp);
}
~Teacher()
{
cout<<"釋放堆區director內存空間1次";
delete[] director;
cin.get();
}
char *show();
protected:
char *director;
};
char *Teacher::show()
{
return director;
}
class Student
{
public:
Student(char *temp,int &pk):teacher(temp),pk(pk),ps(10)
{
number = 1;
score = 100;
}
void show();

protected:
int number;
int score;
Teacher teacher;
int &pk;
const int ps;

};

void Student::show()
{
cout<<teacher.show()<<endl<<number<<endl<<score<<endl<<pk<<endl<<ps<<endl;
}
void main()
{
char *t_name="王大力";
int b=99;
Student a(t_name,b);
a.show();
cin.get();
}



改變之處最重要的在這里Student(char *temp,int &pk):teacher(temp),pk(pk),ps(10)

調用的時候我們使用

Student a(t_name,b);

我們將b的地址傳遞給了int &pk這個引用,使得Student類的引用成員pk和常量成員ps進行了成功的初始化。

但是細心的人會發現,我們在這里使用的初始化方式并不是在構造函數內進行的,而是在外部進行初始化的,的確,在冒號后和在構造函數括號內的效果是一樣的,但和teacher(temp)所不同的是,pk(pk)的括號不是調用函數的意思,而是賦值的意思,我想有些讀者可能不清楚新標準的c++對變量的初始化是答應使用括號方式的,int a=10和int a(10)的等價的,但冒號后是不答應使用=方式只答應()括號方式,所以這里只能使用pk(pk)而不能是pk=pk了。 更多文章 更多內容請看C/C++技術學堂專題,或 這一小節的內容是說對象構造的順序的,對象構造的順序直接關系程序的運行結果,有時候我們寫的程序不錯,但運行出來的結果卻超乎我們的想象,了解c++對對象的構造順序有助于解決這些問題。


c++規定,所有的全局對象和全局變量一樣都在主函數main()之前被構造,函數體內的靜態對象則只構造一次,也就是說只在首次進入這個函數的時候進行構造!

代碼如下:


//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必聞名出處和作者

#include <iostream>
#include <string>
using namespace std;

class Test
{
public:
Test(int a)
{
kk=a;
cout<<"構造參數a:"<<a<<endl;
}
public:
int kk;
};

void fun_t(int n)

{
static Test a(n);
//static Test a=n;//這么寫也是對的
cout<<"函數傳入參數n:"<<n<<endl;
cout<<"對象a的屬性kk的值:"<<a.kk<<endl;
}
Test m(100);
void main()
{
fun_t(20);
fun_t(30);
cin.get();
}




下面我們來看一下,類成員的構造順序的問題。

先看下面的代碼:


//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必聞名出處和作者

#include <iostream>
using namespace std;

class Test
{
public:
Test(int j):pb(j),pa(pb+5)
{

}
public:
int pa;
int pb;
};
void main()
{
Test a(10);
cout<<a.pa<<endl;
cout<<a.pb<<endl;
cin.get();
}


上面的程序在代碼上是沒有任何問題的,但運行結果可能并不如人意。

pa并沒有得到我們所希望的15而是一個隨機的任意地址的值。

這又是為什么呢?

類成員的構造是按照在類中定義的順序進行的,而不是按照構造函數說明后的冒號順序進行構造的,這一點需要記?。。。。?! 更多文章 更多內容請看C/C++技術學堂專題,或

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 名山县| 区。| 中方县| 军事| 白河县| 水富县| 青冈县| 武宁县| 龙川县| 正阳县| 杂多县| 大荔县| 达拉特旗| 玉环县| 思南县| 赣榆县| 黎城县| 拉萨市| 商水县| 甘谷县| 玉山县| 壶关县| 吴桥县| 岳西县| 朝阳区| 太仓市| 望奎县| 湘潭市| 吉木萨尔县| 都昌县| 屯门区| 永平县| 吉木乃县| 家居| 江阴市| 富平县| 沙坪坝区| 安阳县| 伊金霍洛旗| 吉安县| 仁化县|