C++回顧之static成員、static成員函式及類物件大小計算
C中的static使用比較簡單,都不陌生了,C++中static關鍵字在類中的使用需要注意一些細節。static在類中修飾的是資料成員以及成員函式,分別稱之為靜態資料成員及靜態成員函式。
先來看看static靜態資料成員的目的及使用:
(1) 對於特定型別的全體物件而言,有時候可能需要訪問一個全域性的變數。比如說統計某種型別物件已建立的數量。
(2) 如果我們用全域性變數會破壞資料的封裝,一般的使用者程式碼都可以修改這個全域性變數。這時我們可以用類的靜態成員來解決這個問題。
(3) 非靜態資料成員存在於類型別的每個物件中,靜態資料成員則獨立於該類的任意物件而存在,它是與類關聯的物件,不與類物件關聯。
static靜態資料成員的優點:
(1) static靜態資料成員的名字是在類的作用域中,因此可以避免與其它類成員或全域性物件名字衝突。
(2) 可以實施封裝,static靜態資料成員可以是私有的,而全域性物件則不可以。
(3) 閱讀程式容易看出static成員與某個類相關聯,這種可見性可以清晰地反映程式設計師的意圖。
static成員函式需要注意的事項:
(1) static成員函式沒有this指標,因為它是與類關聯的,不與物件關聯
(2) 非靜態成員函式可以訪問靜態成員
(3) 靜態成員函式不可以訪問非靜態成員。
下面舉幾個例子來說明靜態資料成員及靜態成員函式的使用方法。
先來看看下面的統計物件個數的類的定義。
CountedObject.h
#ifndef _COUNTED_OBJECT_H #define _COUNTED_OBJECT_H class CountedObject { public: CountedObject(); ~CountedObject(); public: static int GetCount(); //靜態成員函式 private: static int count_; // 靜態成員函式在定義時有兩步:1 靜態成員的引用性宣告 2 在檔案域上進行定義性說明。 這裡只是第一步 }; #endif
統計物件類的實現, CountedObject.cpp
#include "CountedObject.h"
int CountedObject::count_ = 100; //靜態成員函式定義性宣告方法:1 需要加上類名域運算子 2不需要static關鍵字 3 只能在檔案作用域中進行初始化,不能在類中進行初始化
CountedObject::CountedObject()
{
++count_; //建立物件時自加
}
CountedObject::~CountedObject()
{
--count_; 銷燬物件時自減
}
//靜態成員函式在定義時不加static修飾,只能訪問靜態資料成員,不能訪問普通資料成員
int CountedObject::GetCount()
{
return count_;
}
下面是對靜態資料成員及靜態成員函式的訪問例子
Test1.cpp
#include "CountedObject.h"
#include <iostream>
using namespace std;
int main()
{
//cout << CountedObject::count_ << endl; //靜態資料成員可以用類名來訪問,如果是私有的則不能訪問了,只能提供一個公有介面來訪問
cout << CountedObject::GetCount() << endl; //output:100
CountedObject co1;
cout << CountedObject::GetCount() << endl; //output:101
CountedObject *co2 = new CountedObject;
cout << CountedObject::GetCount() << endl; //output:102
delete co2;
cout << CountedObject::GetCount() << endl; //output:101
return 0;
}
靜態常量資料成員
首先,靜態常量資料成員它屬於靜態的資料成員,與靜態資料成員不同的是,它的值是const,不能被修改。並且靜態常量資料成員也是與類關聯的,全體所有物件共享。普通成員函式及靜態成員函式均可以訪問它。
#include <iostream>
using namespace std;
class Test
{
public:
Test(){}
~Test(){}
public:
//static const int x_;
//1 與static int x_;不同的是,它的初始化可以在類體中進行
//2 但是也只有static const int, static const char(靜態常量整型資料成員)才可以在類體中初始化,如double則不行.
//3 但是靜態常量整型資料成員在類體中初始化方法在VC6.0則是行不通的,VC6.0編譯器比較古老,不支援。
//所以個人還是覺得統一一下,在類體外初始化比較好。
static const int x_ = 100; //靜態常量資料成員
};
//const int Test::x_ = 100;
const int Test::x_;
int main(void)
{
Test t;
cout << t.x_ << endl; //output:100
cout << Test::x_ << endl; //output:100
//Test::x_ = Test::x_ + 1; //Error,不能給常量賦值
//t.x_ = t.x_ + 1; //同上
return 0;
}
下面的例子是關於普通成員函式可以訪問靜態資料成員及靜態成員函式,而靜態成員函式只能訪問靜態資料成員,而不能訪問普通資料成員的例子(原因是靜態成員函式沒有this指標)
#include <iostream>
using namespace std;
class Test
{
public:
Test(int y):y_(y)
{
}
~Test(){}
public:
void TestFun()
{
cout << "x="<< x_ << endl; //非靜態成員函式訪問靜態資料成員
TestStaticFun(); //非靜態成員函式訪問靜態成員函式
}
static void TestStaticFun()
{
//TestFun(); //Error,靜態成員函式不能訪問非靜態成員函式
//cout << "y = "<< y_ << endl; //Error,靜態成員函式不能訪問非靜態成員,因為靜態成員函式沒有隱含的this指標,故無法指向普通物件的非靜態成員。而且也不能訪問非靜態成員函式,因為非靜態成員與非靜態成員函式是屬於物件的,而靜態成員與靜態成員函式是屬於類的,但是物件可以訪問類的靜態成員與靜態成員函式
cout << "TestStaticFun()..x="<<x_<<endl;
}
static int x_; //靜態成員的引用性宣告
int y_;
};
int Test::x_ = 100; //靜態資料成員的定義性說明。
int main()
{
Test t(10);
t.TestFun();
cout << t.x_ << endl; //物件可以訪問靜態成員。這是允許的,但是不推薦,讓人誤解以為x_是屬於t物件的。
cout << Test::x_ << endl;//推薦這麼訪問
return 0;
}
關於類/物件大小的計算
(1) 首先,類大小的計算遵循結構體的對齊原則
(2) 類的大小與普通資料成員有關,與成員函式無關,即普通成員函式,靜態成員函式,靜態資料成員,靜態常量資料成員均對類的大小無影響
(3) 虛擬函式對類的大小有影響(後面再說明),主要是虛表指標帶來的影響
(4) 虛繼承對類的大小有影響(後面再說明),主要是虛基表指標帶來的影響
按照上面的類大小計算原則,對前面一個例子的Test類大小可以進行計算,由於它只有一個普通資料成員y_,靜態資料成員與靜態成員函式,普通成員函式均對類大小無影響,所以前一個例子的類大小為:4bytes,如果不信可以用sizeof(Test)進行計算測試。^_^