深入探究C++ 類成員(Class Members)

来源:https://www.cnblogs.com/ruanchunyi/p/18176001
-Advertisement-
Play Games

一、定義 在class的聲明裡頭,真正有用的兩樣東西是data members 和 member functions: Data members:表示根據這個class所產生的object裡頭會有些什麼東西,它事實上也是占據object記憶體的唯一東西(除非引入虛擬機制)。通常為數據的封裝性,我們把d ...


一、定義

  在class的聲明裡頭,真正有用的兩樣東西是data members 和 member functions:

  Data members:表示根據這個class所產生的object裡頭會有些什麼東西,它事實上也是占據object記憶體的唯一東西(除非引入虛擬機制)。通常為數據的封裝性,我們把data members聲明為private或protected。

  Member functions:是用來處理data members的函數。通常為了界面的開放性,我們把member functions設計為pubilc。

二、Data Members(數據成員)

  Data members的聲明和一般non-class的變數聲明形式一樣,以CPoint為例:

 1 class CPoint
 2 {
 3 public:
 4     CPoint(float x=0.0):_x(x){}
 5     
 6     float x(){return _x;}
 7     void x(float xval){_x=xval;}
 8     
 9 protected:
10     float _x;     //data member
11     
12 };

  第10行的_x就是一個data members。

  如果data member本身不是一個變數,而是一個class object,這種情況比較特殊,稱為composition(組合),而這種object被稱為embedded objects或object member。Composition被用來描述has a的關係(例如汽車有一個引擎),inheritance(繼承)用來描述is a kind of的關係(例如汽車是一種交通工具)。稍後我們會分別描述composition和inheritance兩種情況。

三、Member Functions(成員函數)

  Member functions用來處理class data members。在數據封裝的情況下,member functions可以說就是一個class(或object)的對外界面。這些functions並不占用object的記憶體空間,它被編譯器處理過後,以完全等同一般的global functions的身份出現,獨立於任何object之外。同一份函數代碼如何能夠處理依據同一個class產生出來的不同object的data members呢?關鍵在於有一個this指針隱藏在member functions的參數之中,也隱藏在member functions函數代碼對於data members的處理動作上,是編譯器自動為我們加上去的。

 四、如何取用 Class Members

  要取得class members,如果是在class scope之內,直接使用其名稱即可。但如果是在class scope之外,你有三種方式可以辦到:

  1.透過dot operator(.),例如:

1 CPoint aPoint; // 產生一個 CPoint object,名為 aPoint
2 aPoint.x(); // 喚 起 CPoint::x()
3 aPoint._x = 7.0; // 使 用 CPoint 的 data member

  2.透過arrow operator(->),例如:

1 CPoint* pPoint = new CPoint; // 產生一個 CPoint object,由 pPoint 指向它
2 pPoint->x(); // 喚 起 CPoint::x()
3 pPoint->_x = 7.0; // 使用 CPoint 的 data member

  3.透過scope resolution operator(::),例如:

1 CPoint::foo(); // foo() must be a static member function
2 CPoint::ratio = 0.2; // ratio must be a static data member

  第三種方法未通過任何對象訪問成員,表明這些成員應屬於類層次,即靜態成員(Static Members)。若錯誤地通過對象訪問非靜態成員,如CPoint::foo(),將引發錯誤。靜態成員應通過類名訪問,非靜態成員應通過對象實例訪問。

五、特殊的 Static Members(靜態成員)

  Class中可以使用static關鍵,使members成為靜態。

  靜態的意思是這份members屬於class而非object所有。聽起來有點奇怪,因為class只是屬性,而object才是實體。很難一言以敝之,讓我們將members細分為data 和function來討論。

  當你看完以下的剖析,你會認為static data members和global variables很像,static member functions和global functions 很像。一點沒錯,從功能而言,global 可以取代static。不過,以面對對象的觀點來看,這些members如查在性質上與class息息相關,不是設計成class的static members為佳,面對對象的程式設計中,我們希望global的東西越少越好。

1. Static Data Members(靜態數據成員)

  雖說data屬於object實體所有,但程式設計中有些數據也希望由各object共用,所以提升到class層級比較理想。例如我們設計一個銀行儲戶的class:

1 class SavingAccount
2 {
3 private:
4     char   m_name[40];//儲戶姓名
5     char   m_addr[60];//儲戶地址
6     double m_total;//賬戶金額
7     double m_rate;//利率
8 };

  這家銀行採用浮動利率,每個賬戶的利息是根據當天的利率來計算的,這時m_rate就不適合成為每個object的數據了,否則每天一開市,光把所有的賬戶內容調出來,修改m_rate的值,就花了不少時間。m_rate應該獨立於各object之外,成為class的獨一無二的數據;具體怎麼做?在m_rate前面加上Static關鍵字就行了:

1 class SavingAccount
2 {
3 private:
4     char   m_name[40];//儲戶姓名
5     char   m_addr[60];//儲戶地址
6     double m_total;//賬戶金額
7     Static double m_rate;//利率(註意,這裡只是聲明,不是定義,未分配記憶體)
8 };

  Static data members不屬於object的一部份,而是class的一部份,所以程式可以在還沒有生成任何object的時候就處理Static data members。但首先,你必須定義它(為它分配記憶體)。

你應該在.CPP文件中且在class以外的任何區域設定其初始值,例如在main()之中,或在global function中,或任何函數之外:

1 double SavingAccount::m_rate = 0.0075; // 定義並設定初值
2 void main() // 不指定初值也可以
3 {
4 ...
5 }

  請註意用詞上的嚴謹:上述“定義”動作常被誤以為是“初始化”動作。如果只是單純的初值設定動作,應該可以安排在class constructor中,但上一行絕對無法如此。此外,你也不應該把上述的定義安排於.H文件中,因為這麼一來它可能會被含入許多.CPP文件中,於是就重覆定義了(會發生連接錯誤)。

  上述動作有沒有考慮m_rate是private數據呢?沒有關係,定義static data members,不受任何封裝層級的約束。請註意上述動作也指出static data members的類型,因為這是一個定義動作,不是一個數值指定動作,如果沒有做這個動作,m_rate就沒有獲得記憶體,會發生異常錯誤:

error LNK2001: unresolved external symbol "private: static double
SavingAccount::m_rate"(?m_rate@SavingAccount@@2HA)

  下麵是存取static data members的一種方式,註意:此時還沒有任何object存在:

1 void main()
2 {
3 SavingAccount::m_rate = 0.0072; // 此行要成立,m_rate 需改為public
4 }

   第二種方式是產生一個object後,透過object來處理static data members:

1 void main()
2 {
3 SavingAccount myAccount;
4 myAccount.m_rate = 0.0072; // 此行要成立,m_rate 需改為 public
5 }

  請註意,static data members並不是因為myAccount object的實現而才得以實現,它本來就存在。因此第一種的處理方式不容易給人錯誤印象;

2. Static Menber Functions(靜態成員函數)

  既然static data members 是超越於object之外存在的,一般的member functions能否處理它呢?要知道,,member function得經由某個object才能調用,該object地址則成為所謂的this指針,但由於static data members並不需要靠this指針的指引來決定其地址,所以其實任何函數(包含global函數),只要access level允許,都可以處理static data members。

  但member function得經由某個object才能調用,如果你在尚未產生任何object之前,就希望透過某個member function來更我以為前例的static m_rate,而它又是private(以至於不能被global函數處理)那麼一定要寫個static member function了:

 1 class SavingAccount
 2 {
 3 private:
 4     char   m_name[40];//儲戶姓名
 5     char   m_addr[60];//儲戶地址
 6     double m_total;//賬戶金額
 7     Static double m_rate;//利率(註意,這裡只是聲明,未分配記憶體)
 8 public:
 9     static void setRate(double newRate){m_rate=newRate;}
10 };
11 
12 double SavingAccount::m_rate=0.0072; //設初值
13 
14 int main()
15 {
16     SavingAccount myAccount;
17     //可以透過object調用static member functions
18     myAccount.setRate(0.0072);
19     //也可以直接調用static memberfunction
20     SavingAccount::setRate(0.0072);
21     
22     return 0;
23 }

  由於static member functions 不需要任何object的存就可以被調用,所以編譯器不會為它暗加一個this指針。也因為如此,static member function無法處理任何non-static data members,因為member function之所以能夠以單一一份函數代碼處理各個object的數據而不亂,完全靠的是this指針。

3. Static Object Members(靜態對象成員)

  Object members應該被視為和一般的data members 樣地位,只不過它是class類型,而一般data members是內建類型。當它冠上static,表示它所寄生的class一旦聲明完成(未產生object),並且對static members做了初始化動作,這個static object member就已經在記憶體中有了實體。例如:

  

 1 class Point3d
 2 { public:
 3 // ...
 4 public: // (protected: is better)
 5 static Point3d origin;
 6 float x, y, z;
 7 };
 8 Point3d Point3d::origin; // 別忘了這個動作(其實就是實體配置)
 9 
10 int main()
11 {
12     printf("&Point3d::x = %p\n", &Point3d::x); // 00000004
13     printf("&Point3d::y = %p\n", &Point3d::y); // 00000008
14     printf("&Point3d::z = %p\n", &Point3d::z); // 0000000C
15     printf("&Point3d::origin = %p\n", &Point3d::origin); // 0040A450
16     printf("&Point3d::origin.x = %p\n", &Point3d::origin.x); // 0040A454
17     printf("&Point3d::origin.y = %p\n", &Point3d::origin.y); // 0040A458
18     printf("&Point3d::origin.z = %p\n", &Point3d::origin.z); // 0040A45C
19     Point3d pt3d;
20     cout << "&pt3d.x = " << &pt3d.x << endl;
21     cout << "&pt3d.y = " << &pt3d.y << endl;
22     // 0x0063FDEC
23     // 0x0063FDF0
24     cout << "&pt3d.z = " << &pt3d.z << endl; // 0x0063FDF4
25     
26 }

4. Static Objects(靜態對象)

  static 關鍵字也可以應用在object身上,像這樣:

static CPoint aPoint(3.6);

  aPoint於是就成為一個static object,這個object的生命將持續到整個程式結束為止。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文介紹基於Microsoft SQL Server軟體,實現資料庫表中多種數據查詢方法的具體操作。 目錄1 指定列或全部列查詢——查詢S表學生記錄2 指定列或全部列查詢——查詢學生姓名與出生年份3 按條件查詢及模糊查詢——查詢成績不及格學生學號4 按條件查詢及模糊查詢——查詢20-23歲間學生姓名 ...
  • 在Kafka中,Broker、Topic、Partition和Replication是四個核心概念,它們各自扮演了不同的角色並共同協作以確保數據的可靠性、可擴展性和高性能。以下是關於這四個概念的詳細解釋: Broker(代理) * Broker是Kafka集群中的一個節點,負責存儲和轉發消息。Kaf ...
  • 一、是什麼 當對一個文檔進行佈局(layout)的時候,瀏覽器的渲染引擎會根據標準之一的 CSS 基礎框盒模型(CSS basic box model),將所有元素表示為一個個矩形的盒子(box) 一個盒子由四個部分組成:content、padding、border、margin content,即 ...
  • 本文的目的,是為了讓已經有 Vue2 開發經驗的 人 ,快速掌握 Vue3 的寫法。 因此, 本篇假定你已經掌握 Vue 的核心內容 ,只為你介紹編寫 Vue3 代碼,需要瞭解的內容。 一、Vue3 里 script 的三種寫法 首先,Vue3 新增了一個叫做組合式 api 的東西,英文名叫 Com ...
  • 最近,群里在討論一個很有意思的線條動畫效果,效果大致如下: 簡單而言,就是線條沿著不規則路徑的行進動畫,其中的線條動畫可以理解為是特殊的光效。 本文,我們將一起探索,看看在不使用 JavaScript/Canvas 的基礎上,使用純 CSS/SVG 的方式,我們可以如何大致的還原上述的線條動畫效果。 ...
  • 前言 jquery時代更新視圖是直接對DOM進行操作,缺點是頻繁操作真實 DOM,性能差。react和vue時代引入了虛擬DOM,更新視圖是對新舊虛擬DOM樹進行一層層的遍歷比較,然後找出需要更新的DOM節點進行更新。這樣做的缺點就是如果DOM樹很複雜,在進行新舊DOM樹比較的時候性能就比較差了。那 ...
  • X-Frame-Options 是一個HTTP響應頭,用於控制網頁是否可以嵌套在 <frame>, <iframe>, <embed> 或者 <applet> 中。通過設置 X-Frame-Options 頭部,網站管理員可以防止網頁被嵌套到其他網站的框架中,從而有效防範點擊劫持等安全風險。下麵是關 ...
  • /******************************************************************************************************** * * file name: Zqh_順序表.c * author : keyword2 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...