C++ static、const和static const類型成員變量聲明以及初始化


C++ static、const和static const 以及它們的初始化

const定義的常量在超出其作用域之后其空間會被釋放,而static定義的靜態常量在函數執行后不會釋放其存儲空間。

static表示的是靜態的。類的靜態成員函數、靜態成員變量是和類相關的,而不是和類的具體對象相關的。即使沒有具體對象,也能調用類的靜態成員函數和成員變量。一般類的靜態函數幾乎就是一個全局函數,只不過它的作用域限於包含它的文件中。

在C++中,static靜態成員變量不能在類的內部初始化。在類的內部只是聲明,定義必須在類定義體的外部,通常在類的實現文件中初始化,如:double Account::Rate = 2.25;static關鍵字只能用於類定義體內部的聲明中,定義時不能標示為static

在C++中,const成員變量也不能在類定義處初始化,只能通過構造函數初始化列表進行,並且必須有構造函數。

const數據成員 只在某個對象生存期內是常量,而對於整個類而言卻是可變的。因為類可以創建多個對象,不同的對象其const數據成員的值可以不同。所以不能在類的聲明中初始化const數據成員,因為類的對象沒被創建時,編譯器不知道const數據成員的值是什么。

const數據成員的初始化只能在類的構造函數的初始化列表中進行。要想建立在整個類中都恆定的常量,應該用類中的枚舉常量來實現,或者static cosnt。

class Test{
public:
    Test():a(0){}
    enum {size1=100,size2=200};
private:
    const int a;//只能在構造函數初始化列表中初始化
    static int b;//在類的實現文件中定義並初始化
    conststatic int c;//與 static const int c;相同。
};
 
int Test::b=0;//static成員變量不能在構造函數初始化列表中初始化,因為它不屬於某個對象。
cosnt intTest::c=0;//注意:給靜態成員變量賦值時,不需要加static修飾符,但要加cosnt。

cosnt成員函數主要目的是防止成員函數修改對象的內容。即const成員函數不能修改成員變量的值,但可以訪問成員變量。當方法成員函數時,該函數只能是const成員函數。

static成員函數主要目的是作為類作用域的全局函數。不能訪問類的非靜態數據成員。類的靜態成員函數沒有this指針,這導致:1、不能直接存取類的非靜態成員變量,調用非靜態成員函數2、不能被聲明為virtual

 

關於static、const、static cosnt、const static成員的初始化問題:
1、類里的const成員初始化:
在一個類里建立一個const時,不能給他初值

class foo{
public:
    foo():i(100){}
private:
    const int i=100;//error!!!
};
//或者通過這樣的方式來進行初始化
foo::foo():i(100){}

2、類里的static成員初始化:

類中的static變量是屬於類的,不屬於某個對象,它在整個程序的運行過程中只有一個副本,因此不能在定義對象時 對變量進行初始化,就是不能用構造函數進行初始化,其正確的初始化方法是:

數據類型 類名::靜態數據成員名=值;

class foo{
public:
    foo();
private:
staticint i;
};
 
intfoo::i=20;

這表明:

1、初始化在類體外進行,而前面不加static,以免與一般靜態變量或對象相混淆

2、初始化時不加該成員的訪問權限控制符private、public等

3、初始化時使用作用域運算符來表明它所屬的類,因此,靜態數據成員是類的成員而不是對象的成員。

 

3、類里的static cosnt 和 const static成員初始化
這兩種寫法的作用一樣,為了便於記憶,在此值說明一種通用的初始化方法:

 

class Test{
public:
    static const int mask1;
    conststatic int mask2;
};
constTest::mask1=0xffff;
constTest::mask2=0xffff;
//它們的初始化沒有區別,雖然一個是靜態常量一個是常量靜態。靜態都將存儲在全局變量區域,其實最后結果都一樣。可能在不同編譯器內,不同處理,但最后結果都一樣。

這是一個完整的例子:

#ifdef A_H_
#define A_H_
#include <iostream>
usingnamespace std;
class A{
public:
    A(int a);
    staticvoid print();//靜態成員函數
private:
    static int aa;//靜態數據成員的聲明
    staticconst int count;//常量靜態數據成員(可以在構造函數中初始化)
    const int bb;//常量數據成員
};
 
int A::aa=0;//靜態成員的定義+初始化
const int A::count=25;//靜態常量成員定義+初始化
 
A::A(int a):bb(a){//常量成員的初始化
aa+=1;
}
 
void A::print(){
cout<<"count="<<count<<endl;
cout<<"aa="<<aa<<endl;
}
 
#endif
 
void main(){
    A a(10);
    A::print();//通過類訪問靜態成員函數
    a.print();//通過對象訪問靜態成員函數
}

初始化位置
靜態成員不能在類的定義里初始化(除int外)。不能在頭文件里初始化。
比如定義了 myclass.h,一般放到myclass.cpp里初始化它。

 

C++ 類的靜態成員(static)
靜態成員的提出是為了解決數據共享的問題。實現共享有許多方法,如:設置全局性的變量或對象是一種方法。但是,全局變量或對象是有局限性的。這一章里,我們主要講述類的靜態成員來實現數據的共享。

靜態數據成員
  在類中,靜態成員可以實現多個對象之間的數據共享,並且使用靜態數據成員還不會破壞隱藏的原則,即保證了安全性。因此,靜態成員是類的所有對象中共享的成員,而不是某個對象的成員。

  使用靜態數據成員可以節省內存,因為它是所有對象所公有的,因此,對多個對象來說,靜態數據成員只存儲一處,供所有對象共用。靜態數據成員的值對每個對象都是一樣,但它的值是可以更新的。只要對靜態數據成員的值更新一次,保證所有對象存取更新后的相同的值,這樣可以提高時間效率。

  靜態數據成員的使用方法和注意事項如下:

  1、靜態數據成員在定義或說明時前面加關鍵字static。

  2、靜態成員初始化與一般數據成員初始化不同。靜態數據成員初始化的格式如下:

    <數據類型><類名>::<靜態數據成員名>=<值>

  這表明:

        (1) 初始化在類體外進行,而前面不加static,以免與一般靜態變量或對象相混淆。

  (2) 初始化時不加該成員的訪問權限控制符private,public等。

  (3) 初始化時使用作用域運算符來標明它所屬類,因此,靜態數據成員是類的成員,而不是對象的成員。

  3、靜態數據成員是靜態存儲的,它是靜態生存期,必須對它進行初始化。

  4、引用靜態數據成員時,采用如下格式:

   <類名>::<靜態成員名>

  如果靜態數據成員的訪問權限允許的話(即public的成員),可在程序中,按上述格式來引用靜態數據成員。

  下面舉一例子,說明靜態數據成員的應用:

#include …
class Myclass{
public:
    Myclass(int a, int b, int c);
    void GetNumber();
    void GetSum();
private:
    int A, B, C;
    static int Sum;
};
 
int Myclass::Sum = 0;
 
Myclass::Myclass(int a, int b, int c){
    A = a;
    B = b;
    C = c;
    Sum += A+B+C;
}
 
void Myclass::GetNumber(){
    cout<<"Number="<<a<<","<<b<<","<<c<<endl;
 }
 
void Myclass::GetSum(){
    cout<<"Sum="<<sum<<endl;
 }
 
void main(){
    Myclass M(3, 7, 10),N(14, 9, 11);
    M.GetNumber();
    N.GetNumber();
    M.GetSum();
    N.GetSum();
}

 從輸出結果可以看到Sum的值對M對象和對N對象都是相等的。這是因為在初始化M對象時,將M對象的三個int型數據成員的值求和后賦給了Sum,於是Sum保存了該值。在初始化N對象時,對將N對象的三個int型數據成員的值求和后又加到Sum已有的值上,於是Sum將保存另后的值。所以,不論是通過對象M還是通過對象N來引用的值都是一樣的,即為54。

靜態成員函數
靜態成員函數和靜態數據成員一樣,它們都屬於類的靜態成員,它們都不是對象成員。因此,對靜態成員的引用不需要用對象名。

  在靜態成員函數的實現中不能直接引用類中說明的非靜態成員,可以引用類中說明的靜態成員。如果靜態成員函數中要引用非靜態成員時,可通過對象來引用。下面通過例子來說明這一點。

#include …
class M{
public:
    M(int a){
        A=a;
        B+=a;
    }
    
    static void f1(M m);
private:
    int A;
    static int B;
};
 
void M::f1(M m)
{
    cout<<"A="<<m.a<<endl;
    cout<<"B="<<b<<endl;
}
 
int M::B=0;
 
void main()
{
    M P(5),Q(10);
    M::f1(P); //調用時不用對象名
    M::f1(Q);
}

讀者可以自行分析其結果。從中可看出,調用靜態成員函數使用如下格式:

  <類名>::<靜態成員函數名>(<參數表>);


 
C++中各種類型的成員變量的初始化方法
c++各種不同類型成員根據是否static、時候const類型的初始化方法不盡相同,java的語法就沒有這么復雜,怪的得那么多人都跑去學Java了。以前面試時被人問到這個問題回答不出來,寫代碼時也經常搞亂了,這里翻了下書,總結一下。

-----------------Test.h----------------------------
#pragma once
class Test
{
private :
    int var1;
    // int var11= 4; 錯誤的初始化方法
    const int var2 ;
    // const int var22 =22222; 錯誤的初始化方法
    static int var3;
    // static int var3333=33333; 錯誤,只有靜態常量int成員才能直接賦值來初始化
    static const int var4=4444; //正確,靜態常量成員可以直接初始化    
    static const int var44;
public:
    Test(void);
    ~Test(void);
};
--------------------Test.cpp-----------------------------------
#include ".\test.h"
 
int Test::var3 = 3333333; //靜態成員的正確的初始化方法
 
// int Test::var1 = 11111;; 錯誤靜態成員才能初始化
// int Test::var2 = 22222; 錯誤
// int Test::var44 = 44444; // 錯誤的方法,提示重定義
Test::Test(void):var1(11111),var2(22222)正確的初始化方法//var3(33333)不能在這里初始化
{
    var1 =11111; //正確, 普通變量也可以在這里初始化
    //var2 = 222222; 錯誤,因為常量不能賦值,只能在 “constructor initializer (構造函數的初始化列表)” 那里初始化
          
    var3 =44444; //這個賦值是正確的,不過因為所有對象一個靜態成員,所以會影響到其他的,這不能叫做初始化了吧
}
Test::~Test(void){}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

有些成員變量的數據類型比較特別,它們的初始化方式也和普通數據類型的成員變量有所不同。這些特殊的類型的成員變量包括:

    a. 常量型成員變量
    b. 引用型成員變量
    c. 靜態成員變量
    d. 整型靜態常量成員變量
    e. 非整型靜態常量成員變量


  對於常量型成員變量和引用型成員變量的初始化,必須通過構造函數初始化列表的方式進行。在構造函數體內給常量型成員變量和引用型成員變量賦值的方式是行不通的。

    靜態成員變量的初始化也頗有點特別。
   參考下面的代碼以及其中注釋:

// Initialization of Special Data Member
#include <iostream>
using namespace std;     
 
class BClass
{
public:
    BClass() : i(1),ci(2), ri(i) // 對於常量型成員變量和引用型成員變量,必須通過    
    {                // 參數化列表的方式進行初始化。在構造函數體內進行賦值的方式,是行不通的。
    } 
 
    voidprint_values()
    {
        cout<< "i =\t" << i << endl;
        cout<< "ci =\t" << ci << endl;
        cout<< "ri =\t" << ri << endl;
        cout<< "si =\t" << si << endl;
        cout<< "csi =\t" << csi << endl;
        cout<< "csi2 =\t" << csi2 << endl;
        cout<< "csd =\t" << csd << endl;
    }
 
private:
    inti; // 普通成員變量
    constint ci; // 常量成員變量
    int&ri; // 引用成員變量
    staticint si; // 靜態成員變量
    //staticint si2 = 100; // error: 只有靜態常量成員變量,才可以這樣初始化
    staticconst int csi; // 靜態常量成員變量
    staticconst int csi2 = 100; // 靜態常量成員變量的初始化(Integral type) (1)
    staticconst double csd; // 靜態常量成員變量(non-Integral type)
    //staticconst double csd2 = 99.9; // error: 只有靜態常量整型數據成員才可以在類中初始化
};
// 靜態成員變量的初始化(Integral type)
int BClass::si = 0;
// 靜態常量成員變量的初始化(Integral type)
const int BClass::csi = 1;
// 靜態常量成員變量的初始化(non-Integral type)
const double BClass::csd = 99.9;
 
// 在初始化(1)中的csi2時,根據Stanley B. Lippman的說法下面這行是必須的。
// 但在VC2003中如果有下面一行將會產生錯誤,而在VC2005中,下面這行則可有可無,這個和編譯器有關。
const int BClass::csi2;
 
int main(void)
{
    BClassb_class;
    b_class.print_values(); 
    return0;
}

c++靜態成員小結,

c++,static

類中的靜態成員真是個讓人愛恨交加的特性。我曾經在面試時,被主考官抓住這個問題一陣窮追猛打,直把我問的面紅耳赤,敗下陣來。所以回來之后,我痛定思痛,決定好好總結一下靜態類成員的知識點,以便自己在以后面試中,在此類問題上不在被動。 

靜態類成員包括靜態數據成員和靜態函數成員兩部分。 

靜態數據成員
類體中的數據成員的聲明前加上static關鍵字,該數據成員就成為了該類的靜態數據成員。和其他數據成員一樣,靜態數據成員也遵守public/protected/private訪問規則。同時,靜態數據成員還具有以下特點: 

1.靜態數據成員的定義。 
靜態數據成員實際上是類域中的全局變量。所以,靜態數據成員的定義(初始化)不應該被放在頭文件中。 (聲明vs.定義(初始化))
其定義方式與全局變量相同。舉例如下: 

xxx.h文件 
class base{ 
private: 
    static const int _i;//聲明,標准c++支持有序類型在類體中初始化,但vc6不支持。 
}; 
 
xxx.cpp文件 
const int base::_i=10;//定義(初始化)時不受private和protected訪問限制. 

注:不要試圖在頭文件中定義(初始化)靜態數據成員。在大多數的情況下,這樣做會引起重復定義這樣的錯誤。即使加上#ifndef #define #endif或者#pragma once也不行。 

2.靜態數據成員被類的所有對象所共享,包括該類派生類的對象。即派生類對象與基類對象共享基類的靜態數據成員。舉例如下: 

class base{ 
public : 
    static int _num;//聲明 
}; 
int base::_num=0;//靜態數據成員的真正定義 
 
class derived:public base{ }; 
 
main() 
{ 
    base a; 
    derived b; 
    a._num++; 
    cout<<"base class static data number_num is"<<a._num<<endl; 
    b._num++; 
    cout<<"derived class static datanumber _num is"<<b._num<<endl; 
} 
// 結果為1,2;可見派生類與基類共用一個靜態數據成員。 

3.靜態數據成員可以成為成員函數的可選參數,而普通數據成員則不可以。舉例如下: 

class base{ 
public : 
    static int _staticVar; 
    int _var; 
    void foo1(int i=_staticVar);//正確,_staticVar為靜態數據成員 
    void foo2(int i=_var);//錯誤,_var為普通數據成員 
}; 

4.★靜態數據成員的類型可以是所屬類的類型,而普通數據成員則不可以。普通數據成員的只能聲明為所屬類類型的指針或引用。舉例如下: 

class base{ 
public : 
    static base _object1;//正確,靜態數據成員 
    base _object2;//錯誤 
    base *pObject;//正確,指針 
    base &mObject;//正確,引用 
}; 

5.★這個特性,我不知道是屬於標准c++中的特性,還是vc6自己的特性。 
靜態數據成員的值在const成員函數中可以被合法的改變。舉例如下: 

class base{ 
public: 
    base(){
        _i=0;
        _val=0;
    } 
 
    mutable int _i; 
    static int _staticVal; 
    int _val; 
    void test() const{//const 成員函數 
        _i++;//正確,mutable數據成員 
        _staticVal++;//正確,static數據成員 
        _val++;//錯誤 
    } 
}; 
int base::_staticVal=0; 

靜態成員函數 

靜態成員函數沒有什么太多好講的。 

1.靜態成員函數的地址可用普通函數指針儲存,而普通成員函數地址需要用類成員函數指針來儲存。舉例如下: 

class base{ 
    static int func1(); 
    int func2(); 
}; 
 
int (*pf1)()=&base::func1;//普通的函數指針 
int (base::*pf2)()=&base::func2;//成員函數指針 

2.靜態成員函數不可以調用類的非靜態成員。因為靜態成員函數不含this指針。 

3.靜態成員函數不可以同時聲明為 virtual、const、volatile函數。舉例如下: 

class base{ 
    virtual static void func1();//錯誤 
    static void func2() const;//錯誤 
    static void func3() volatile;//錯誤 
}; 

 

 

最后要說的一點是,靜態成員是可以獨立訪問的,也就是說,無須創建任何對象實例就可以訪問。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM