Ⅰ c語言 類的構造函數和析構函數(考試在線等!急!!!)
類是編程人員表達自定義數據類型的C++機制。它和C語言中的結構類似,C++類
支持數據抽象和面向對象的程序設計,從某種意義上說,也就是數據類型的設
計和實現。
一、類的設計
1.類的聲明
class 類名
{
private: //私有
...
public: //公有
...
};
2.類的成員
一般在C++類中,所有定義的變數和函數都是類的成員。如果是變數,我們就叫
它數據成員如果是函數,我們就叫它成員函數。
3.類成員的可見性
private和public訪問控制符決定了成員的可見性。由一個訪問控制符設定的可
訪問狀態將一直持續到下一個訪問控制符出現,或者類聲明的結束。私有成員
僅能被同一個類中的成員函數訪問,公有成員既可以被同一類中的成員函數訪
問,也可以被其他已經實例化的類中函數訪問。當然,這也有例外的情況,這
是以後要討論的友元函數。
類中默認的數據類型是private,結構中的默認類型是public。一般情況下,變
量都作為私有成員出現,函數都作為公有成員出現。
類中還有一種訪問控制符protected,叫保護成員,以後再說明。
4.初始化
在聲明一個類的對象時,可以用圓括弧()包含一個初始化表。
看下面一個例子:
#include iostream.h
class Box
{
private:
int height,width,depth; //3個私有數據成員
public:
Box(int,int,int);
~Box();
int volume(); //成員函數
};
Box::Box(int ht,int wd,int dp)
{
height=ht;
width=wd;
depth=dp;
}
Box::~Box()
{
//nothing
}
int Box::volume()
{
return height*width*depth;
}
int main()
{
Box thisbox(3,4,5); //聲明一個類對象並初始化
cout< return 0;
}
當一個類中沒有private成員和protected成員時,也沒有虛函數,並且不是從
其他類中派生出來的,可以用{}來初始化。(以後再講解)
5.內聯函數
內聯函數和普通函數的區別是:內聯函數是在編譯過程中展開的。通常內聯函
數必須簡短。定義類的內聯函數有兩種方法:一種和C語言一樣,在定義函數時
使用關鍵字inline。如:
inline int Box::volume()
{
return height*width*depth;
}
還有一種方法就是直接在類聲明的內部定義函數體,而不是僅僅給出一個函數
原型。我們把上面的函數簡化一下:
#include iostream.h
class Box
{
private:
int height,width,depth;
public:
Box(int ht,int wd,int dp)
{
height=ht;
width=wd;
depth=dp;
}
~Box();
int volume()
{
return height*width*depth;
}
};
int main()
{
Box thisbox(3,4,5); //聲明一個類對象並初始化
cout< return 0;
}
這樣,兩個函數都默認為內聯函數了。
Ⅱ C語言裡面構造函數和析構函數的運用辦法
摘 要:構造函數與析構函數是一個類中看似較為簡單的兩類函數,但在實際運用過程中總會出現一些意想不到的運行錯誤。本文將較系統的介紹構造函數與析構函數的原理及在C#中的運用,以及在使用過程中需要注意的若幹事項。
關鍵字:構造函數;析構函數;垃圾回收器;非託管資源;託管資源
一.構造函數與析構函數的原理
作為比C更先進的語言,C#提供了更好的機制來增強程序的安全性。C#編譯器具有嚴格的類型安全檢查功能,它幾乎能找出程序中所有的語法問題,這的確幫了程序員的大忙。但是程序通過了編譯檢查並不表示錯誤已經不存在了,在「錯誤」的大家庭里,「語法錯誤」的地位只能算是冰山一角。級別高的錯誤通常隱藏得很深,不容易發現。
根據經驗,不少難以察覺的程序錯誤是由於變數沒有被正確初始化或清除造成的,而初始化和清除工作很容易被人遺忘。微軟利用面向對象的概念在設計C#語言時充分考慮了這個問題並很好地予以解決:把對象的初始化工作放在構造函數中,把清除工作放在析構函數中。當對象被創建時,構造函數被自動執行。當對象消亡時,析構函數被自動執行。這樣就不用擔心忘記對象的初始化和清除工作。
二.構造函數在C#中的運用
構造函數的名字不能隨便起,必須讓編譯器認得出才可以被自動執行。它的命名方法既簡單又合理:讓構造函數與類同名。除了名字外,構造函數的另一個特別之處是沒有返回值類型,這與返回值類型為void的函數不同。如果它有返回值類型,那麼編譯器將不知所措。在你可以訪問一個類的方法、屬性或任何其它東西之前, 第一條執行的語句是包含有相應類的構造函數。甚至你自己不寫一個構造函數,也會有一個預設構造函數提供給你。
class TestClass
{
public TestClass(): base() {} // 由CLR提供
}
下面列舉了幾種類型的構造函數
1)預設構造函數
class TestClass
{
public TestClass(): base() {}
}
上面已介紹,它由系統(CLR)提供。
2)實例構造函數
實例構造函數是實現對類中實例進行初始化的方法成員。如:
using System;
class Point
{
public double x, y;
public Point()
{
this.x = 0;
this.y = 0;
}
public Point(double x, double y)
{
this.x = x;
this.y = y;
}
}
class Test
{
static void Main()
{
Point a = new Point();
Point b = new Point(3, 4); // 用構造函數初始化對象
}
}
聲明了一個類Point,它提供了兩個構造函數。它們是重載的。一個是沒有參數的Point構造函數和一個是有兩個double參數的Point構造函數。如果類中沒有提供這些構造函數,那麼會CLR會自動提供一個預設構造函數的。但一旦類中提供了自定義的構造函數,如Point()和Point(double x, double y),則預設構造函數將不會被提供,這一點要注意。
3) 靜態構造函數
靜態構造函數是實現對一個類進行初始化的方法成員。它一般用於對靜態數據的初始化。靜態構造函數不能有參數,不能有修飾符而且不能被調用,當類被載入時,類的靜態構造函數自動被調用。如:
using System.Data;
class Employee
{
private static DataSet ds;
static Employee()
{
ds = new DataSet(...);
}
}
聲明了一個有靜態構造函數的類Employee。注意靜態構造函數只能對靜態數據成員進行初始化,而不能對非靜態數據成員進行初始化。但是,非靜態構造函數既可以對靜態數據成員賦值,也可以對非靜態數據成員進行初始化。
如果類僅包含靜態成員,你可以創建一個private的構造函數:private TestClass() {…},但是private意味著從類的外面不可能訪問該構造函數。所以,它不能被調用,且沒有對象可以被該類定義實例化。
以上是幾種類型構造函數的簡單運用,下面將重點介紹一下在類的層次結構中(即繼承結構中)基類和派生類的構造函數的使用方式。派生類對象的初始化由基類和派生類共同完成:基類的成員由基類的構造函數初始化,派生類的成員由派生類的構造函數初始化。
當創建派生類的對象時,系統將會調用基類的構造函數和派生類的構造函數,構 造函數的執行次序是:先執行基類的構造函數,再執行派生類的構造函數。如果派生類又有對象成員,則,先執行基類的構造函數,再執行成員對象類的構造函數,最後執行派生類的構造函數。
至於執行基類的什麼構造函數,預設情況下是執行基類的無參構造函數,如果要執行基類的有參構造函數,則必須在派生類構造函數的成員初始化表中指出。如:
class A
{ private int x;
public A( ) { x = 0; }
public A( int i ) { x = i; }
};
class B : A
{ private int y;
public B( ) { y = 0; }
public B( int i ) { y = i; }
public B( int i, int j ):A(i) { y = j; }
};
B b1 = new B(); //執行基類A的構造函數A(),再執行派生類的構造函數B()
B b2 = new B(1); //執行基類A的構造函數A(),再執行派生類的構造函數B(int)
B b3 = new B(0,1); //執行執行基類A的構造函數A(int) ,再執行派生類的
構造函數B(int,int)
在這里構造函數的執行次序是一定要分析清楚的。另外,如果基類A中沒有提供無參構造函數public A( ) { x = 0; },則在派生類的所有構造函數成員初始化表中必須指出基類A的有參構造函數A(i),如下所示:
class A
{ private int x;
public A( int i ) { x = i; }
};
class B : A
{ private int y;
public B():A(i) { y = 0; }
public B(int i):A(i) { y = i; }
public B(int i, int j):A(i) { y = j; }
};
三.析構函數和垃圾回收器在C#中的運用
析構函數是實現銷毀一個類的實例的方法成員。析構函數不能有參數,不能任何修飾符而且不能被調用。由於析構函數的目的與構造函數的相反,就加前綴『~』以示區別。
雖然C#(更確切的說是CLR)提供了一種新的內存管理機制---自動內存管理機制(Automatic memory management),資源的釋放是可以通過「垃圾回收器」 自動完成的,一般不需要用戶干預,但在有些特殊情況下還是需要用到析構函數的,如在C#中非託管資源的釋放。
資源的.釋放一般是通過"垃圾回收器"自動完成的,但具體來說,仍有些需要注意的地方:
1. 值類型和引用類型的引用其實是不需要什麼"垃圾回收器"來釋放內存的,因為當它們出了作用域後會自動釋放所佔內存,因為它們都保存在棧(Stack)中;
2. 只有引用類型的引用所指向的對象實例才保存在堆(Heap)中,而堆因為是一個自由存儲空間,所以它並沒有像"棧"那樣有生存期("棧"的元素彈出後就代表生存期結束,也就代表釋放了內存),並且要注意的是,"垃圾回收器"只對這塊區域起作用;
然而,有些情況下,當需要釋放非託管資源時,就必須通過寫代碼的方式來解決。通常是使用析構函數釋放非託管資源,將用戶自己編寫的釋放非託管資源的代碼段放在析構函數中即可。需要注意的是,如果一個類中沒有使用到非託管資源,那麼一定不要定義析構函數,這是因為對象執行了析構函數,那麼"垃圾回收器"在釋放託管資源之前要先調用析構函數,然後第二次才真正釋放託管資源,這樣一來,兩次刪除動作的花銷比一次大多的。下面使用一段代碼來示析構函數是如何使用的:
public class ResourceHolder
{
~ResourceHolder()
{
// 這里是清理非託管資源的用戶代碼段
}
}
四.小結
構造函數與析構函數雖然是一個類中形式上較簡單的函數,但它們的使用決非看上去那麼簡單,因此靈活而正確的使用構造函數與析構函數能夠幫你更好的理解CLR的內存管理機制,以及更好的管理系統中的資源。
Ⅲ c++ 析構函數 是在什麼時候執行
c++ 析構函數調用時間:
1、對象生命周期結束,被銷毀時;
2、delete指向對象的指針時,或delete指向對象的基類類型指針,而其基類虛構函數是虛函數時;
3、對象i是對象o的成員,o的析構函數被調用時,對象i的析構函數也被調用。
C++是C語言的繼承,它既可以進行C語言的過程化程序設計,又可以進行以抽象數據類型為特點的基於對象的程序設計,還可以進行以繼承和多態為特點的面向對象的程序設計。C++擅長面向對象程序設計的同時,還可以進行基於過程的程序設計,因而C++就適應的問題規模而論,大小由之。
C++不僅擁有計算機高效運行的實用性特徵,同時還致力於提高大規模程序的編程質量與程序設計語言的問題描述能力。
(3)c語言模擬析構函數擴展閱讀:
C++語言特點:
1、支持繼承和重用
在C++現有類的基礎上可以聲明新類型,這就是繼承和重用的思想。通過繼承和重用可以更有效地組織程序結構,明確類間關系,並且充分利用已有的類來完成更復雜、深入的開發。新定義的類為子類,成為派生類。它可以從父類那裡繼承所有非私有的屬性和方法,作為自己的成員。
2、支持多態性
採用多態性為每個類指定表現行為。多態性形成由父類和它們的子類組成的一個樹型結構。在這個樹中的每個子類可以接收一個或多個具有相同名字的消息。當一個消息被這個樹中一個類的一個對象接收時,這個對象動態地決定給予子類對象的消息的某種用法。多態性的這一特性允許使用高級抽象。
繼承性和多態性的組合,可以輕易地生成一系列雖然類似但獨一無二的對象。由於繼承性,這些對象共享許多相似的特徵。由於多態性,一個對象可有獨特的表現方式,而另一個對象有另一種表現方式。
參考資料來源:網路-C++
Ⅳ c語言中怎樣使用析構函數 不是c++裡面的內容僅僅是C語言中怎樣使用析構函數
C語言是面向過程的編程語言,構造函數和析構函數是用在面向對象的編程語言里的。所以C裡面是沒有析構函數的!
Ⅳ 如何用c語言實現CString的構造函數,析構函數和賦值函數
類是編程人員表達自定義數據類型的C++機制。它和C語言中的結構類似,C++類
支持數據抽象和面向對象的程序設計,從某種意義上說,也就是數據類型的設
計和實現。
那麼
String
類的原型如下
class
String
{
public:
String(const
char
*str=NULL);
//構造函數
String(const
String
&other);
//拷貝構造函數
~String(void);
//析構函數
String&
operator=(const
String
&other);
//等號操作符重載,賦值函數
ShowString();
private:
char
*m_data;
//字元指針
};
String::~String()
{
delete
[]
m_data;
//析構函數,釋放地址空間
}
String::String(const
char
*str)
{
if
(str==NULL)//當初始化串不存在的時候,為m_data申請一個空間存放'/0';
{
m_data=new
char[1];
*m_data='/0';
}
else//當初始化串存在的時候,為m_data申請同樣大小的空間存放該串;
{
int
length=strlen(str);
m_data=new
char[length+1];
strcpy(m_data,str);
}
}
String::String(const
String
&other)//拷貝構造函數,功能與構造函數類似。
{
int
length=strlen(other.m_data);
m_data=new
[length+1];
strcpy(m_data,other.m_data);
}
String&
String::operator
=(const
String
&other)
//賦值函數
{
if
(this==&other)//當地址相同時,直接返回;
return
*this;
delete
[]
m_data;//當地址不相同時,刪除原來申請的空間,重新開始構造;
int
length=sizeof(other.m_data);
m_data=new
[length+1];
strcpy(m_data,other.m_data);
return
*this;
}
String::ShowString()//由於m_data是私有成員,對象只能通過public成員函數來訪問;
{
cout<<this->m_data<<endl;
}
測試一下:
main()
{
String
AD;
char
*
p="ABCDE";
String
B(p);
AD.ShowString();
AD=B;
AD.ShowString();
}
Ⅵ C語言析構函數問題
構造函數錯了,不是析構函數錯了,看下面:
#include<iostream>//包含頭文件iostream
usingnamespacestd;//使用命名空間std
classCourse
{
public:
Course(intpId,char*pName,intpHours,char*pTeacherInCharge);
voidprintCourse();
~Course();
private:
intid;
char*name;
inthours;
char*teacherInCharge;
};
Course::Course(intpId,char*pName,intpHours,char*pTeacherInCharge)
{
id=pId;
hours=pHours;
name=newchar[strlen(pName)+1];
if(name!=NULL)strcpy(name,pName);//name=pName;
teacherInCharge=newchar[strlen(pTeacherInCharge)+1];
if(teacherInCharge!=NULL)strcpy(teacherInCharge,pTeacherInCharge);//teacherInCharge=pTeacherInCharge;
}
voidCourse::printCourse()
{
cout<<"ID"<<"NAME"<<"HOURS"<<"TEACHINCHARGE"<<endl;
cout<<id<<name<<hours<<teacherInCharge<<endl;
}
Course::~Course()
{
cout<<"destructing……"<<endl;
delete[]name;
delete[]teacherInCharge;
}
intmain()
{
Coursec[3]={Course(0001,"lili",36,"zhangsan"),Course(0002,"sary",36,"wanger"),Course(0003,"mary",36,"zhaoqian")};
for(inti=0;i<3;i++)
c[i].printCourse();
return0;
}
Ⅶ 關於C++中析構函數析構順序問題
a 和c的作用域都是整個main函數中
也可以說是整個程序!
顯示a的有參數構造
然後是b的有參數構造
b的析構-----------b的作用域只在特定的函數中,函數結束,b就會析構!
c的有參構造
c的析構
a的析構
程序運行結束!~
其實先構造的後解析,是棧的先進後出的原理,
所有的函數的調用都會將參數入棧和局部變數入棧!
出棧的時候是相反的順序!