1. 程式人生 > >c++類詳解:訪問許可權,建構函式,拷貝建構函式,解構函式

c++類詳解:訪問許可權,建構函式,拷貝建構函式,解構函式

類的定義

類可以看做是一種資料型別,類這種資料型別是一個包含成員變數和成員函式的集合。類的成員變數和普通變數一樣,也有資料型別和名稱,佔用固定長度的記憶體。但是,在定義類的時候不能對成員變數賦值,因為類只是一種資料型別或者說是一種模板,本身不佔用記憶體空間,而變數的值則需要記憶體來儲存。
類的成員函式也和普通函式一樣,都有返回值和引數列表,它與一般函式的區別是:成員函式是一個類的成員,出現在類體中,它的作用範圍由類來決定;而普通函式是獨立的,作用範圍是全域性的,或位於某個名稱空間內。例項1:

#include <iostream>
using namespace std;

//類通常定義在函式外面
class Student{
public:
	//類包含的變數
	char *name;
	int age;
	float score;
	//類包含的函式
	void say()
	{
		cout << name << "的年齡是" << age << ",成績是" << score << endl;
	}
};

int main(){
	//建立物件
	Student stu;
	stu.name = "小明";
	stu.age = 15;
	stu.score = 92.5f;
	stu.say();
	return 0;
}
例項1中在類體中定義了成員函式,也可以只在類體中宣告函式,在類體中直接定義函式時,不需要在函式名前面加上類名,因為函式屬於哪一個類是很明顯的。但當成員函式定義在類外時,就必須在函式名前面加上類名予以限定。::被稱為域解析符(也稱作用域運算子或作用域限定符),用來連線類名和函式名,指明當前函式屬於哪個類。
成員函式必須先在類體中作原型宣告,然後在類外定義,也就是說類體的位置應在函式定義之前。
class Student{
public:
	//成員變數
	char *name;
	int age;
	float score;
	//成員函式
	void say();  //函式宣告
};
//函式定義
void Student::say(){
	cout << name << "的年齡是" << age << ",成績是" << score << endl;
}

雖然 C++ 支援將行內函數定義在類的外部,但建議將函式定義在類的內部,這樣它會自動成為行內函數,何必費力不討好地將它定義在類的外部呢,這樣並沒有任何優勢。類的定義可以使用指標形式,並使用“->”初始化,例項2:

#include <iostream>
using namespace std;

class Student{
public:
	char *name;
	int age;
	float score;

	void say()
	{
		cout << name << "的年齡是" << age << ",成績是" << score << endl;
	}
};

int main(){
	Student *pStu = new Student;
	pStu->name = "小明";
	pStu->age = 15;
	pStu->score = 92.5f;
	pStu->say();
	delete pStu;  //刪除物件
	return 0;
}
例項2在堆上建立物件,需要使用new關鍵字,在棧上創建出來的物件都有一個名字,比如 stu,使用指標指向它不是必須的。但是通過 new 創建出來的物件就不一樣了,它在堆上分配記憶體,沒有名字,只能得到一個指向它的指標,所以必須使用一個指標變數來接收這個指標,否則以後再也無法找到這個物件了,更沒有辦法使用它。也就是說,使用 new 在堆上創建出來的物件是匿名的,沒法直接使用,必須要用一個指標指向它,再借助指標來訪問它的成員變數或成員函式。

類成員的訪問許可權:

C++通過 public、protected、private 三個關鍵字來控制成員變數和成員函式的訪問許可權,它們分別表示公有的、受保護的、私有的,被稱為成員訪問限定符。所謂訪問許可權,就是你能不能使用該類中的成員。C++ 中的 public、private、protected 只能修飾類的成員,不能修飾類,C++中的類沒有共有私有之分。
在類的內部(定義類的程式碼內部),無論成員被宣告為 public、protected 還是 private,都是可以互相訪問的,沒有訪問許可權的限制。在類的外部(定義類的程式碼之外),只能通過物件訪問成員,並且通過物件只能訪問 public 屬性的成員,不能訪問 private、protected 屬性的成員。見例項3:
#include <iostream>
using namespace std;
//類的宣告
class Student{
private:  //私有的
	char *m_name;
	int m_age;
	float m_score;
public:  //共有的
	void setname(char *name)
	{
		m_name = name;
	}
	void setage(int age)
	{
		m_age = age;
	}
	void setscore(float score)
	{
		m_score = score;
	}
	void show()
	{
		cout << m_name << "的年齡是" << m_age << ",成績是" << m_score << endl;
	}
};

int main(){
	//在棧上建立物件
	Student stu;
	stu.setname("小明");
	stu.setage(15);
	stu.setscore(92.5f);
	stu.show();
	//在堆上建立物件
	Student *pstu = new Student;
	pstu->setname("小紅");
	pstu->setage(16);
	pstu->setscore(96);
	pstu->show();
	return 0;
}
成員變數大都以m_開頭,這是約定成俗的寫法,不是語法規定的內容。以m_開頭既可以一眼看出這是成員變數,又可以和成員函式中的形參名字區分開。例項3中的m_name、m_age、m_score 是私有成員變數,不能在類外部通過物件訪問。如下定義就是一種錯誤的表示:
Student stu;
stu.m_name = "小明";
stu.m_age = 15;
stu.m_score = 92.5f;
stu.show();
這種將成員變數宣告為 private、將部分成員函式宣告為 public 的做法體現了類的封裝性。所謂封裝,是指儘量隱藏類的內部實現,只向用戶提供有用的成員函式。private 關鍵字的作用在於更好地隱藏類的內部實現,該向外暴露的介面(能通過物件訪問的成員)都宣告為 public,不希望外部知道、或者只在類內部使用的、或者對外部沒有影響的成員,都建議宣告為 private。
根據C++軟體設計規範,實際專案開發中的成員變數以及只在類內部使用的成員函式(只被成員函式呼叫的成員函式)都建議宣告為 private,而只將允許通過物件呼叫的成員函式宣告為 public。另外還有一個關鍵字 protected,宣告為 protected 的成員在類外也不能通過物件訪問,但是在它的派生類內部可以訪問。
宣告為 private 的成員和宣告為 public 的成員的次序可以是任意的,既可以先出現 private 部分,也可以先出現 public 部分。如果既不寫 private 也不寫 public,預設為 private。在一個類體中,private 和 public 可以分別出現多次。但是為了使程式清晰,應該養成使每一種成員訪問限定符在類定義體中只出現一次的習慣。

類的建構函式:

在C++中,有一種特殊的成員函式,它的名字和類名相同,沒有返回值,不需要使用者顯式呼叫(使用者也不能呼叫),而是在建立物件時自動執行。這種特殊的成員函式就是建構函式。例項3中,通過成員函式 setname()、setage()、setscore() 分別為成員變數 name、age、score 賦值,這樣做雖然有效,但顯得有點麻煩。有了建構函式,我們就可以簡化這項工作,在建立物件的同時為成員變數賦值。見例項4:
#include <iostream>
using namespace std;
class Student
{
private:
	char *m_name;
	int m_age;
	float m_score;
public:
	//宣告建構函式
	Student(char *name, int age, float score);
	//宣告普通成員函式
	void show();
};
//定義建構函式
Student::Student(char *name, int age, float score)
{
	m_name = name;
	m_age = age;
	m_score = score;
}
//定義普通成員函式
void Student::show()
{
	cout << m_name << "的年齡是" << m_age << ",成績是" << m_score << endl;
}
int main()
{
	//建立物件時向建構函式傳參
	Student stu("小明", 15, 92.5f);
	stu.show();
	//建立物件時向建構函式傳參
	Student *pstu = new Student("小紅", 16, 96);
	pstu->show();
	return 0;
}
例項4,在 Student 類中定義了一個建構函式Student(char *, int, float),它的作用是給三個 private 屬性的成員變數賦值。要想呼叫該建構函式,就得在建立物件的同時傳遞實參,並且實參由( )包圍,和普通的函式呼叫非常類似。在棧上建立物件時,實參位於物件名後面,例如Student stu("小明", 15, 92.5f);在堆上建立物件時,實參位於類名後面,例如new Student("小紅", 16, 96)。
建構函式必須是 public 屬性的,否則建立物件時無法呼叫。設定為 private、protected 屬性不會報錯,但沒有意義。建構函式沒有返回值,因為沒有變數來接收返回值,即使有也毫無用處,這意味著:(1)不管是宣告還是定義,函式名前面都不能出現返回值型別,即使是 void 也不允許;(2)函式體中不能有 return 語句。
建構函式的呼叫是強制性的,一旦在類中定義了建構函式,那麼建立物件時就一定要呼叫,不呼叫是錯誤的。如果有多個過載的建構函式,那麼建立物件時提供的實參必須和其中的一個建構函式匹配;反過來說,建立物件時只有一個建構函式會被呼叫。
例項4中的程式碼,如果寫作Student stu或者new Student就是錯誤的,因為類中包含了建構函式,而建立物件時卻沒有呼叫。

如果使用者沒有定義建構函式,那麼編譯器會自動生成一個預設的建構函式,只是這個建構函式的函式體是空的,沒有形參,也不執行任何操作。比如上面的 Student 類,預設生成的建構函式如下:Student(){}。
一個類必須有建構函式,要麼使用者自己定義,要麼編譯器自動生成。一旦使用者自己定義了建構函式,不管有幾個,也不管形參如何,編譯器都不再自動生成。和普通成員函式一樣,建構函式是允許過載的。一個類可以有多個過載的建構函式,建立物件時根據傳遞的實參來判斷呼叫哪一個建構函式。

#include <iostream>
using namespace std;
class Student
{
private:
	char *m_name;
	int m_age;
	float m_score;
public:
	Student();
	Student(char *name, int age, float score);
	void setname(char *name);
	void setage(int age);
	void setscore(float score);
	void show();
};
Student::Student()
{
	m_name = NULL;
	m_age = 0;
	m_score = 0.0;
}
Student::Student(char *name, int age, float score)
{
	m_name = name;
	m_age = age;
	m_score = score;
}
void Student::setname(char *name)
{
	m_name = name;
}
void Student::setage(int age)
{
	m_age = age;
}
void Student::setscore(float score)
{
	m_score = score;
}
void Student::show()
{
	if (m_name == NULL || m_age <= 0)
	{
		cout << "成員變數還未初始化" << endl;
	}
	else
	{
		cout << m_name << "的年齡是" << m_age << ",成績是" << m_score << endl;
	}
}
int main(){
	//呼叫建構函式 Student(char *, int, float)
	Student stu("小明", 15, 92.5f);
	stu.show();
	//呼叫建構函式 Student()
	Student *pstu = new Student();
	pstu->show();
	pstu->setname("小紅");
	pstu->setage(16);
	pstu->setscore(96);
	pstu->show();
	return 0;
}

類的拷貝建構函式:

拷貝建構函式是一種特殊的建構函式,函式的名稱必須和類名稱一致,它唯一的一個引數是本型別的一個引用變數,該引數是const型別,不可變的。例如:類X的拷貝建構函式的形式為X(X& x)。
#include <iostream>  
using namespace std;

class CExample {
private:
	int a;
public:
	//建構函式  
	CExample(int b)
	{
		a = b;
	}
	//拷貝建構函式  
	CExample(const CExample& C)
	{
		a = C.a;
	}
	//一般函式  
	void Show()
	{
		cout << "a的值為:"<<a<< endl;
	}
};

int main()
{
	CExample A(10);
	CExample B = A; // CExample B(A); 也是一樣的  
	B.Show();
	return 0;
}
上面的例子中,CExample(const CExample& C) 就是自定義的拷貝建構函式。拷貝分為淺拷貝和深拷貝兩種,淺拷貝,指的是在物件複製時,只對物件中的資料成員進行簡單的賦值,預設拷貝建構函式執行的也是淺拷貝。大多情況下“淺拷貝”已經能很好地工作了,但是一旦物件存在了動態成員,那麼淺拷貝就會出問題了。在某些狀況下,類內成員變數需要動態開闢堆記憶體,如果實行淺拷貝,即把物件裡的值完全複製給另一個物件,如A=B。這時,如果B中有一個成員變數指標已經申請了記憶體,那A中的那個成員變數也指向同一塊記憶體。這就出現了問題:當B把記憶體釋放了(如:析構),這時A內的指標就是野指標了,出現執行錯誤。舉個錯誤的例子:
class Rect  
{  
public:  
    Rect()      // 建構函式,p指向堆中分配的一空間  
    {  
        p = new int(100);  
    }  
    ~Rect()     // 解構函式,釋放動態分配的空間  
    {  
        if(p != NULL)  
        {  
            delete p;  
        }  
    }  
private:  
    int width;  
    int height;  
    int *p;     // 一指標成員  
};  
  
int main()  
{  
    Rect rect1;  
    Rect rect2(rect1);   // 複製物件  
    return 0;  
}  
上面例子中,執行的是淺拷貝:

  我們需要的不是兩個p有相同的值,而是兩個p指向的空間有相同的值,解決辦法就是使用“深拷貝”。深拷貝是指:如果一個類擁有資源,當這個類的物件發生複製過程的時候,資源重新分配,這個過程就是深拷貝。正確的例子為:
class Rect  
{  
public:  
    Rect()      // 建構函式,p指向堆中分配的一空間  
    {  
        p = new int(100);  
    }  
    Rect(const Rect& r)  
    {  
        width = r.width;  
        height = r.height;  
        p = new int;    // 為新物件重新動態分配空間  
        *p = *(r.p);  
    }  
    ~Rect()     // 解構函式,釋放動態分配的空間  
    {  
        if(p != NULL)  
        {  
            delete p;  
        }  
    }  
private:  
    int width;  
    int height;  
    int *p;     // 一指標成員  
};  

此時rect1的p和rect2的p各自指向一段記憶體空間,但它們指向的空間具有相同的內容,這就是所謂的“深拷貝”。

類的解構函式(Destructor)

解構函式(destructor) 與建構函式相反,當物件結束其生命週期時(例如物件所在的函式已呼叫完畢),系統自動執行解構函式。解構函式往往用來做“清理善後” 的工作(例如在建立物件時用new開闢了一片記憶體空間,應在退出前在解構函式中用delete釋放)。觸發條件:當物件被銷燬時,會自動呼叫解構函式,釋放資源。建構函式在物件例項化時自動呼叫,解構函式在物件銷燬時自動呼叫。
解構函式(Destructor)也是一種特殊的成員函式,沒有返回值,不需要程式設計師顯式呼叫(程式設計師也沒法顯式呼叫),而是在銷燬物件時自動執行。建構函式的名字和類名相同,而解構函式的名字是在類名前面加一個“~”符號。解構函式沒有引數,不能被過載,因此一個類只能有一個解構函式。如果使用者沒有定義,編譯器會自動生成一個預設的解構函式。

#include <string.h>
#include <iostream>

using namespace std;

class stud
{
private://私有部分
	int num;
	char name[10];
	char sex;
public://公用部分
	stud(int n, const char nam[], char s)//建構函式
	{
		num = n;
		strcpy_s(name, nam);
		sex = s;
	}

	~stud() //解構函式
	{
		cout << "stud has been destructed!" << endl;//通過輸出提示告訴我們解構函式確實被呼叫了
	}

	void display()//成員函式,輸出物件的資料
	{
		cout << "num:" << num << endl;
		cout << "name:" << name << endl;
		cout << "sex:" << sex << endl;
	}
};
int main()
{
	stud stud1(10010, "Wangli", 'f'), stud2(10011, "Zhangfun", 'm');//建立兩個物件
	stud1.display();//輸出學生1的資料
	stud2.display();//輸出學生2的資料
	return 0;
}//主函式結束的同時,物件stud1,stud2均應被“清理”,而清理就是通過呼叫了解構函式實現的。

參考:

http://www.cnblogs.com/mr-wid/archive/2013/02/18/2916309.html

http://c.biancheng.net/cpp/biancheng/view/185.html

http://blog.csdn.net/lwbeyond/article/details/6202256/

http://baike.baidu.com/link?url=R4MhPpouXzfx9uUINgnUeSnWDzQCEnzLFwy41wmqEjiasMGrxXXI2Ogk6rAcSnpSlCEw_MvLPhQ90NhAXvk23CM2sntilP9UufOMzZ-8bPgqP-XMqrTSDhhCS_92qGu0

http://blog.csdn.net/qq_34803572/article/details/55213551

相關推薦

c++訪問許可權建構函式拷貝建構函式函式

類的定義 類可以看做是一種資料型別,類這種資料型別是一個包含成員變數和成員函式的集合。類的成員變數和普通變數一樣,也有資料型別和名稱,佔用固定長度的記憶體。但是,在定義類的時候不能對成員變數賦值,因為類只是一種資料型別或者說是一種模板,本身不佔用記憶體空間,而變數的值則需要

C++

1、類的定義 class 類名稱 { public:      公有成員(外部介面,可被使用該類的所有程式碼所使用) private:      私有成員 (只允許本類中的函式訪問,而類外部的任何

C++中的一些細節(過載、重寫、覆蓋、隱藏建構函式函式拷貝建構函式、賦值函式在繼承時的一些問題)

1 函式的過載、重寫(重定義)、函式覆蓋及隱藏 其實函式過載與函式重寫、函式覆蓋和函式隱藏不是一個層面上的概念。前者是同一個類內,或者同一個函式作用域內,同名不同引數列表的函式之間的關係。而後三者是基類和派生類函式不同情況下的關係。 1.1 函式過載

C#的方法過載從寫虛擬函式抽象函式隱藏基方法!

    CSDN廣告是越來越多了,所有部落格筆記不再更新,新網址 DotNet筆記 1:過載   方法過載的主要好處就是,不用為了對不同的引數型別或引數個數,而寫多個函式。       特點:函式名稱必須一樣,返回型別可以不一樣,引數可以不一樣。 using System

C#、方法的訪問修飾符

屬性 tro nbsp 程序 .net art 支持 分析 方法 這篇文章主要介紹了C#類的訪問修飾符用法,較為詳細的分析了C#類的訪問修飾符概念與用法,具有一定的參考借鑒價值,需要的朋友可以參考下 本文詳細分析了C#類的訪問修飾符用法,分享給大家供大家參考。具體用法分析

17-10-5.c#的使用與訪問........對的調用還有些模糊..

c# 訪問 1-1 -1 png blog 使用 logs .cn 17-10-5.c#類的使用與訪問........對類的調用還有些模糊..

Java程式設計思想 第六章訪問許可權控制

一個優秀的程式設計師是通過不斷的重構程式碼讓自己的程式變得更加易用、可讀和完善的。在重構修改的過程中,如果是一個類庫編寫人員,那麼怎麼樣保證自己修改的部分不會影響到客戶端編寫人員(即使用這個類庫的程式設計師)呢?同時也要避免他們對自己類庫內部的程式進行改動。Java中提供了訪問許可權控制的概

C# 方法和屬性的訪問許可權修飾符解析

       在C#中,方法和屬性共有default、public、private、protected、internal、protected internal六種訪問許可權修飾符。 1.  defa

java(有關成員變數的訪問許可權

private,public,protected,預設不寫(firendly) 1、Class類的訪問許可權: public:可以供所有的類訪問。 預設:預設可以稱為friendly但是,java語言中是沒有friendly這個修飾符的,這樣稱呼應該是

C++物件空指標訪問成員函式

題目: class A{ public: void test(){printf("test A");} }; int main(){ A*pA=NULL; pA->test(); } 結果是輸出“test A”而不是程式崩潰,原因如下: 一種解

C++面試時應該實現的string(建構函式拷貝建構函式、賦值運算子過載和解函式

一、string類的4個基本函式是什麼? 建構函式 拷貝建構函式 賦值運算子過載 解構函式 二、函式實現 1.建構函式 String(char* pStr = " ")

讀書筆記實現string建構函式拷貝建構函式函式、過載運算子=

#include <iostream> #include <cassert> #include <cstring> using namespace std; class MyString{ public: MyString(co

C++物件空指標訪問成員函式(靜態繫結)

題目: class A{ public: void test(){printf("test A");} }; int main(){ A*pA=NULL; pA->test(); } 結果是輸出“test A”而不是

Java程式設計思想讀書筆記——第六章訪問許可權控制

第六章 訪問許可權控制 初學Java的時候很納悶,為什麼要提供各種訪問修飾,都用public不就行了,程式都能執行,還多省事 我感覺如果這個程式只有你自己去編寫,去維護,那麼其實訪問許可權可以忽略 但是,比如說我寫了一個第三方開源庫,有很多很多的人在使用我的庫,那麼如

從原理上理解如何由震源機制一個節面的strike,dip,rake可以求出另一個節面的

方向 矢量 不難 角度 image 技術 log 表達 分享 首先,需要回到最原始的地震矩的表達式: 已知strike,dip,rake 根據strike和dip可以求出v,根據strike,dip,rake,可以求出u。 把求出來的v和u互換,相當於原來的位錯矢量變成法

C++建構函式2——拷貝建構函式

前言:拷貝建構函式是C++中的重點之一,在這裡對其知識進行一個簡單的總結。 一、什麼是拷貝建構函式 在C++中,對於內建型別的變數來說,在其建立的過程中用同類型的另一個變數來初始化它是完全可以的,如: 1 int value=100; 2 int new_value=value;//在變

建構函式函式拷貝建構函式、賦值函式

 類的四種基本預設函式:建構函式、解構函式、拷貝建構函式、賦值函式。 建構函式:建立類時,系統自動呼叫建構函式,用以初始化,分配空間,沒有自定義的建構函式時,系統預設一個無引數的建構函式。 class book { private:     int isBook;

編寫String的建構函式拷貝建構函式函式和賦值函式

class String { public: String(const char *str = NULL); // 普通建構函式 String(const String &other); // 拷貝建構函式 ~String(void);

編寫String 的建構函式拷貝建構函式函式和賦值函式

 編寫類String 的建構函式、解構函式和賦值函式,已知類String 的原型為: class String { public:String(const char *str = NULL); // 普通建構函式String(const String &other

C++ String建構函式拷貝建構函式的實現

建構函式、解構函式與賦值函式是每個類最基本的函式,在一些公司的面試中也會經常問到這方面的問題。每個類只有一個解構函式和一個賦值函式,但可以有多個建構函式(包含一個拷貝建構函式,其它的稱為普通建構函式)。對於任意一個類A,如果不手動編寫上述函式,C++編譯器將自動為類A生成四