日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++类和对象详解

發布時間:2023/12/13 c/c++ 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++类和对象详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

創建對象

兩種創建對象的方式:一種是在棧上創建,形式和定義普通變量類似;另外一種是在堆上使用 new 關鍵字創建,必須要用一個指針指向它,讀者要記得 delete 掉不再使用的對象。

通過對象名字訪問成員使用點號.,通過對象指針訪問成員使用箭頭->,這和結構體非常類似。

成員變量和函數

類可以看做是一種數據類型,它類似于普通的數據類型,但是又有別于普通的數據類型。類這種數據類型是一個包含成員變量和成員函數的集合。

類的成員變量和普通變量一樣,也有數據類型和名稱,占用固定長度的內存。但是,在定義類的時候不能對成員變量賦值,因為類只是一種數據類型或者說是一種模板,本身不占用內存空間,而變量的值則需要內存來存儲。

類的成員函數也和普通函數一樣,都有返回值和參數列表,它與一般函數的區別是:成員函數是一個類的成員,出現在類體中,它的作用范圍由類來決定;而普通函數是獨立的,作用范圍是全局的,或位于某個命名空間內。
?

class Student{ public://成員變量char *name;int age;float score;//成員函數void say(){cout<<name<<"的年齡是"<<age<<",成績是"<<score<<endl;} };

但我們一般建議類內只是聲明函數,類外定義。?

class Student{ public://成員變量char *name;int age;float score;//成員函數void say(); //函數聲明 };//函數定義 void Student::say(){cout<<name<<"的年齡是"<<age<<",成績是"<<score<<endl; }

在類體中直接定義函數時,不需要在函數名前面加上類名,因為函數屬于哪一個類是不言而喻的。
但當成員函數定義在類外時,就必須在函數名前面加上類名予以限定。::被稱為域解析符(也稱作用域運算符或作用域限定符),用來連接類名和函數名,指明當前函數屬于哪個類。

在類體中和類體外定義成員函數的區別

在類體中和類體外定義成員函數是有區別的:在類體中定義的成員函數會自動成為內聯函數,在類體外定義的不會。當然,在類體內部定義的函數也可以加 inline 關鍵字,但這是多余的,因為類體內部定義的函數默認就是內聯函數。

內聯函數一般不是我們所期望的,它會將函數調用處用函數體替代,所以我建議在類體內部對成員函數作聲明,而在類體外部進行定義,這是一種良好的編程習慣,實際開發中大家也是這樣做的。

當然,如果你的函數比較短小,希望定義為內聯函數,那也沒有什么不妥的。

如果你既希望將函數定義在類體外部,又希望它是內聯函數,那么可以在定義函數時加 inline 關鍵字。當然你也可以在函數聲明處加 inline,不過這樣做沒有效果,編譯器會忽略函數聲明處的 inline

構造函數

#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; }

該例在 Student 類中定義了一個構造函數Student(char *, int, float),它的作用是給三個 private 屬性的成員變量賦值。要想調用該構造函數,就得在創建對象的同時傳遞實參,并且實參由( )包圍,和普通的函數調用非常類似。

在棧上創建對象時,實參位于對象名后面,例如Student stu("小明", 15, 92.5f);在堆上創建對象時,實參位于類名后面,例如new Student("李華", 16, 96)。

構造函數必須是 public 屬性的,否則創建對象時無法調用。當然,設置為 private、protected 屬性也不會報錯,但是沒有意義。

構造函數沒有返回值,因為沒有變量來接收返回值,即使有也毫無用處,這意味著:

  • 不管是聲明還是定義,函數名前面都不能出現返回值類型,即使是 void 也不允許;
  • 函數體中不能有 return 語句。

構造函數的重載

和普通成員函數一樣,構造函數是允許重載的。一個類可以有多個重載的構造函數,創建對象時根據傳遞的實參來判斷調用哪一個構造函數。

構造函數的調用是強制性的,一旦在類中定義了構造函數,那么創建對象時就一定要調用,不調用是錯誤的。如果有多個重載的構造函數,那么創建對象時提供的實參必須和其中的一個構造函數匹配;反過來說,創建對象時只有一個構造函數會被調用。

默認構造函數

如果用戶自己沒有定義構造函數,那么編譯器會自動生成一個默認的構造函數,只是這個構造函數的函數體是空的,也沒有形參,也不執行任何操作。比如上面的 Student 類,默認生成的構造函數如下:

Student(){}

一個類必須有構造函數,要么用戶自己定義,要么編譯器自動生成。

一旦用戶自己定義了構造函數,不管有幾個,也不管形參如何,編譯器都不再自動生成。

實際上編譯器只有在必要的時候才會生成默認構造函數,而且它的函數體一般不為空。默認構造函數的目的是幫助編譯器做初始化工作,而不是幫助程序員。這是C++的內部實現機制,這里不再深究,初學者可以按照上面說的“一定有一個空函數體的默認構造函數”來理解。

最后需要注意的一點是,調用沒有參數的構造函數也可以省略括號。對于示例的代碼,在棧上創建對象可以寫作Student stu()或Student stu,在堆上創建對象可以寫作Student *pstu = new Student()或Student *pstu = new Student,它們都會調用構造函數 Student()。

以前我們就是這樣做的,創建對象時都沒有寫括號,其實是調用了默認的構造函數。

初始化列表

構造函數的一項重要功能是對成員變量進行初始化,可以在構造函數的函數體中對成員變量一一賦值,還可以采用初始化列表。

#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){//TODO: }

如本例所示,定義構造函數時并沒有在函數體中對成員變量一一賦值,其函數體為空(當然也可以有其他語句),而是在函數首部與函數體之間添加了一個冒號:,后面緊跟m_name(name), m_age(age), m_score(score)語句,這個語句的意思相當于函數體內部的m_name = name; m_age = age; m_score = score;語句,也是賦值的意思。

使用構造函數初始化列表并沒有效率上的優勢,僅僅是書寫方便,尤其是成員變量較多時,這種寫法非常簡單明了。

注意,成員變量的初始化順序與初始化列表中列出的變量的順序無關,它只與成員變量在類中聲明的順序有關。

創建對象時系統會自動調用構造函數進行初始化工作,同樣,銷毀對象時系統也會自動調用一個函數來進行清理工作,例如釋放分配的內存、關閉打開的文件等,這個函數就是析構函數。


析構函數

?

析構函數(Destructor)也是一種特殊的成員函數,沒有返回值,不需要程序員顯式調用(程序員也沒法顯式調用),而是在銷毀對象時自動執行。構造函數的名字和類名相同,而析構函數的名字是在類名前面加一個~符號。

注意:析構函數沒有參數,不能被重載,因此一個類只能有一個析構函數。如果用戶沒有定義,編譯器會自動生成一個默認的析構函數。

#include <iostream> using namespace std;class VLA{ public:VLA(int len); //構造函數~VLA(); //析構函數 public:void input(); //從控制臺輸入數組元素void show(); //顯示數組元素 private:int *at(int i); //獲取第i個元素的指針 private:const int m_len; //數組長度int *m_arr; //數組指針int *m_p; //指向數組第i個元素的指針 };VLA::VLA(int len): m_len(len){ //使用初始化列表來給 m_len 賦值if(len > 0){ m_arr = new int[len]; /*分配內存*/ }else{ m_arr = NULL; } } VLA::~VLA(){delete[] m_arr; //釋放內存 } void VLA::input(){for(int i=0; m_p=at(i); i++){ cin>>*at(i); } } void VLA::show(){for(int i=0; m_p=at(i); i++){if(i == m_len - 1){ cout<<*at(i)<<endl; }else{ cout<<*at(i)<<", "; }} } int * VLA::at(int i){if(!m_arr || i<0 || i>=m_len){ return NULL; }else{ return m_arr + i; } }int main(){//創建一個有n個元素的數組(對象)int n;cout<<"Input array length: ";cin>>n;VLA *parr = new VLA(n);//輸入數組元素cout<<"Input "<<n<<" numbers: ";parr -> input();//輸出數組元素cout<<"Elements: ";parr -> show();//刪除數組(對象)delete parr;return 0; }

析構函數的執行時機

析構函數在對象被銷毀時調用,而對象的銷毀時機與它所在的內存區域有關。
在所有函數之外創建的對象是全局對象,它和全局變量類似,位于內存分區中的全局數據區,程序在結束執行時會調用這些對象的析構函數。
在函數內部創建的對象是局部對象,它和局部變量類似,位于棧區,函數執行結束時會調用這些對象的析構函數。
new 創建的對象位于堆區,通過 delete 刪除時才會調用析構函數;如果沒有 delete,析構函數就不會被執行。

#include <iostream> #include <string> using namespace std;class Demo{ public:Demo(string s);~Demo(); private:string m_s; }; Demo::Demo(string s): m_s(s){ } Demo::~Demo(){ cout<<m_s<<endl; }void func(){//局部對象Demo obj1("1"); }//全局對象 Demo obj2("2");int main(){//局部對象Demo obj3("3");//new創建的對象Demo *pobj4 = new Demo("4");func();cout<<"main"<<endl;return 0; }

this 是?一個關鍵字,也是一個 const指針,它指向當前對象,通過它可以訪問當前對象的所有成員。

所謂當前對象,是指正在使用的對象。例如對于stu.show();,stu 就是當前對象,this 就指向 stu。

注意,this 是一個指針,要用->來訪問成員變量或成員函數。
this 雖然用在類的內部,但是只有在對象被創建以后才會給 this 賦值,并且這個賦值的過程是編譯器自動完成的,不需要用戶干預,用戶也不能顯式地給 this 賦值。

this 原理

this 實際上是成員函數的一個形參,在調用成員函數時將對象的地址作為實參傳遞給 this。不過 this 這個形參是隱式的,它并不出現在代碼中,而是在編譯階段由編譯器默默地將它添加到參數列表中。


this 作為隱式形參,本質上是成員函數的局部變量,所以只能用在成員函數的內部,并且只有在通過對象調用成員函數時才給 this 賦值。

成員函數最終被編譯成與對象無關的普通函數,除了成員變量,會丟失所有信息,所以編譯時要在成員函數中添加一個額外的參數,把當前對象的首地址傳入,以此來關聯成員函數和成員變量。這個額外的參數,實際上就是 this,它是成員函數和成員變量關聯的橋梁。

靜態變量

有時候我們希望在多個對象之間共享數據,對象 a 改變了某份數據后對象 b 可以檢測到。共享數據的典型使用場景是計數,以前面的 Student 類為例,如果我們想知道班級中共有多少名學生,就可以設置一份共享的變量,每次創建對象時讓該變量加 1。

class Student{ public:Student(char *name, int age, float score);void show(); public:static int m_total; //靜態成員變量 private:char *m_name;int m_age;float m_score; };

static 成員變量屬于類,不屬于某個具體的對象,即使創建多個對象,也只為 m_total 分配一份內存,所有對象使用的都是這份內存中的數據。當某個對象修改了 m_total,也會影響到其他對象。


注意:static 成員變量的內存既不是在聲明類時分配,也不是在創建對象時分配,而是在(類外)初始化時分配。反過來說,沒有在類外初始化的 static 成員變量不能使用。

static 成員變量既可以通過對象來訪問,也可以通過類來訪問。

//通過類類訪問 static 成員變量 Student::m_total = 10; //通過對象來訪問 static 成員變量 Student stu("小明", 15, 92.5f); stu.m_total = 20; //通過對象指針來訪問 static 成員變量 Student *pstu = new Student("李華", 16, 96); pstu -> m_total = 20;

總結:

1)?一個類中可以有一個或多個靜態成員變量,所有的對象都共享這些靜態成員變量,都可以引用它。

2) static 成員變量和普通 static 變量一樣,都在內存分區中的全局數據區分配內存,到程序結束時才釋放。這就意味著,static 成員變量不隨對象的創建而分配內存,也不隨對象的銷毀而釋放內存。而普通成員變量在對象創建時分配內存,在對象銷毀時釋放內存。

3) 靜態成員變量必須初始化,而且只能在類體外進行。例如:

int Student::m_total = 10;

初始化時可以賦初值,也可以不賦值。如果不賦值,那么會被默認初始化為 0。全局數據區的變量都有默認的初始值 0,而動態數據區(堆區、棧區)變量的默認值是不確定的,一般認為是垃圾值。

4) 靜態成員變量既可以通過對象名訪問,也可以通過類名訪問當通過對象名訪問時,對于不同的對象,訪問的是同一份內存。

靜態成員函數

靜態成員函數與普通成員函數的根本區別在于:普通成員函數有 this 指針,可以訪問類中的任意成員;而靜態成員函數沒有 this 指針,只能訪問靜態成員(包括靜態成員變量和靜態成員函數)。

C++ class和struct區別

cpp中保留了C語言的 struct 關鍵字,并且加以擴充。在C語言中,struct 只能包含成員變量,不能包含成員函數。而在C++中,struct 類似于 class,既可以包含成員變量,又可以包含成員函數。

C++中的 struct 和 class 基本是通用的,唯有幾個細節不同:

  • 使用 class 時,類中的成員默認都是 private 屬性的;而使用 struct 時,結構體中的成員默認都是 public 屬性的。
  • class 繼承默認是 private 繼承,而 struct 繼承默認是 public 繼承
  • class 可以使用模板,而 struct 不能

C++ 沒有拋棄C語言中的 struct 關鍵字,其意義就在于給C語言程序開發人員有一個歸屬感,并且能讓C++編譯器兼容以前用C語言開發出來的項目。

在編寫C++代碼時,我強烈建議使用 class 來定義類,而使用 struct 來定義結構體,這樣做語義更加明確。

總結

以上是生活随笔為你收集整理的C++类和对象详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。