1. 程式人生 > >const、extern、explicit關鍵字 靜態變數(static宣告、未命名的名稱空間、類中使用列舉宣告的變數) this指標

const、extern、explicit關鍵字 靜態變數(static宣告、未命名的名稱空間、類中使用列舉宣告的變數) this指標

1、const關鍵字

     (1)可以用const來確保方法不修改引數:

Star::Star(const char * s){...}   //won't change the string to which s points

     (2)可以用const來確保方法不修改呼叫它的物件:

void Star::show()const{...}   //won't change invoking object

這裡const表示const Star * this,而this指向呼叫的物件

     (3)通常,可以將返回引用的函式放在賦值語句的左側,這實際意味著可以將值賦給引用的物件。但可以使用const來確保引用或指標返回的值不能用於修改物件中的資料:

const Stock & Stock::topval(const Stock & s)const
{
    if(s.total_bal>total_val)
        return s;    //argument object
    else
        return *this;    //invoking object
}

       該方法返回this或s的引用。因為this和s都被宣告為const,所有函式不能對他們進行修改,這意味著返回的引用也必須被宣告為const。

        注意,如果函式將引數宣告為指向const的指標或引用,則不能將該引數傳遞給另一個函式,除非後者也確保了引數不會被修改。

 

//version 1
Vector Max(const Vector & v1,const Vector & v2)
{
    if(v1.magval()>v2.magval())
        return v1;
    else
        return v2;
}

//version 2
const Vector & Max(const Vector & v1,const Vector & v2)
{
    if(v1.magval()>v2.magval())
        return v1;
    else
        return v2;
}
  • 返回物件將呼叫它的複製建構函式,而返回引用不會。呼叫複製建構函式時,可能會建立一個新的臨時物件,在該臨時變數過期時會自動呼叫解構函式將其刪除釋放記憶體。
  • v2和v2都被宣告為const引用,因此返回型別必須為const,這樣才匹配。
  • 如果被返回的物件是被呼叫函式中的區域性變數,則不應按引用方式返回它,因為在被呼叫函式執行完畢時,區域性物件將呼叫解構函式。

 

2、靜態變數

//twofile1.cpp
#include<iostream>
int tom=3;
int dick=30;
static int harry=300;

void remote_access();

int main()
{
    using namespace std;
    cout<<"main() reports the following addresses: \n";
    cout<<&tom<<" = &tom, "<<&dick<<" =&dick , ";
    cout<<&harry<<" =&harry, \n";
    remote_access();
    return 0;
}



//twofile2.cpp

#include<iostream>
extern int tom;
static int dick=10;
int harry=200;

void remote_access()
{
    using namespace std;
    cout<<"remote_access() reports the following addresses: \n";
    cout<<&tom<<" =&tom, "<<&dick<<" =&dick, ";
    cout<<&harry<<" =&harry\n";
}
  •        關鍵字static被用在作用域為整個檔案的宣告時,表示內部連結性;被用於區域性宣告中時,表示區域性變數的儲存持續性為靜態的。
  •       如果宣告的靜態變數的名稱與另一個檔案中宣告的常規變數相同,則在該檔案中,靜態變數將隱藏常規變數。
  •       在C++標準中,不贊成在名稱空間和全域性作用域中使用static關鍵字(該標準使用“不贊成”表明,這種做法目前合法,但以後修訂標準時,很可能將其視為非法)。          
  •       在預設情況下全域性變數的連結性為外部的,但const全域性變數的連結性為內部的。即,在C++中使用const關鍵字就像是 使用static關鍵字一樣。在宣告const變數時,必須進行初始化。與常規變數不同的是,可以初始化extern const變數。       如:extern const int states=50; 
  •       宣告的外部變數,其作用域為當前檔案,表示內部連結性;但是可以使用extern關鍵字訪問其他檔案中宣告的外部變數。
  •       不能在多個檔案中定義同一個全域性變數。即,只能有一個檔案可以包含前面的宣告,而其他檔案中必須使用關鍵字extern來提供引用宣告。另外,只有未使用extern關鍵字的宣告才能被初始化。

       

       (2) 不能在未命名名稱空間所屬檔案之外的其他檔案中,使用該名稱空間中的名稱,因此這種方法可以替代連線性為內部的靜態變數。通常在呼叫的函式的中宣告靜態變數。

        故,假設有如下程式碼: 

static int counts;
int other();

int main()
{
    ...
}

int other()
{
    ...
}

按照C++標準,程式設計師應該這樣書寫程式碼:

namespace
{
   int counts;
}
int other();

int main()
{
    ...
}

int other()
{
    ...
}

       在未命名的名稱空間中宣告的名稱的潛在作用域為:從宣告點到該宣告點區域末尾。(功能同全域性變數)

 

       using宣告使一個名稱可用,而using編譯指令(由名稱空間和它前面的關鍵字using namespace組成)使所有的名稱都可用。

如:using std::cout;//using 宣告

       using namespace std;//using 編譯指令

 (3)    類中使用列舉宣告的變數,等同於使用static宣告的靜態全域性變數。

如:

class Stonewt
{
private:
    enum{Lbs_per_stn=14};
    int stone;
    double pds_left;
    double pounds;
public:
    Stonewt(double lbs);
    Stonewt(int stn,double lbs);
    Stonewt();
    ~Stonewt();
    void show_lbs()const;
    void show_stn()const;
};

    其中的"enum{Lbs_per_stn=14};"等同於"static const int Lbs_per_stn=14;"

(4)    靜態類static的深入理解:

//stringBad.h
#include<iostream>
class StringBad
{
private:
    char * str; //pointer to string
    int len;    //length of string
    static int num_strings; //number of objects
    //enum{num_strings2=0};
    //static const int num_strings3=0;
public:
    StringBad(const char * s);
    StringBad();
    ~StringBad();
    friend std::ostream & operator<<(std::ostream & os,
                                     const StringBad & st);
};



//stringBad.cpp
#include<cstring>
#include"strngbad.h"
using std::cout;

int StringBad::num_strings=0;

StringBad::StringBad(const char *s)
{
    len=std::strlen(s);
    str=new char[len+1];
    std::strcpy(str,s);
    num_strings++;
    cout<<num_strings<<": \""<<str
       <<"\" object created\n";

}

StringBad::StringBad()
{
    len=4;
    str=new char[4];
    std::strcpy(str,"C++");
    num_strings++;
    cout<<num_strings<<": \""<<str
       <<"\"default object created\n";
}

StringBad::~StringBad()
{
    cout<<"\""<<str<<"\" objet deleted, ";
    --num_strings;
    cout<<num_strings<<" left\n";
    delete [] str;
}

std::ostream & operator<<(std::ostream & os,const StringBad & st)
{
    os<<st.str;
    return os;
}



//useStringBad.cpp
#include<iostream>
#include "strngbad.h"

using std::cout;

void callme1(StringBad &);
void callme2(StringBad &);

int main()
{
    using std::endl;
    StringBad headline1("Celery Stalks at Midnight");
    StringBad headline2("Lettuce Prey");
    StringBad sports("Spinach Leaves Bowl for Dollars");
    cout<<"headline1: "<<headline1<<endl;
    cout<<"headline2: "<<headline2<<endl;
    cout<<"sports: "<<sports<<endl;
    callme1(headline1);
    cout<<"headline1: "<<headline1<<endl;
    callme2(headline2);
    cout<<"headline2: "<<headline2<<endl;
    cout<<"Initialize one object to another: \n";
    StringBad sailor=sports;
    cout<<"sallor: "<<sailor<<endl;
    cout<<"Assign one object to another: \n";
    StringBad knot;
    knot=headline1;
    cout<<"knot: "<<knot<<endl;
    cout<<"End of main()\n";
    return 0;
}

void callme1(StringBad & rsb)
{
    cout<<"String passed by reference: \n";
    cout<<" \""<<rsb<<"\"\n";
}

void callme2(StringBad & sb)
{
    cout<<"String passed by value: \n";
    cout<<" \""<<sb<<"\"\n";
}
  • 不能在類宣告中初始化靜態資料成員,除非靜態資料成員為列舉或static const int valuename;
  • 字串獨立儲存在堆記憶體中,物件僅儲存了指出到哪裡去查詢字串的資訊。
  • 類成員str是指標,建構函式必須分配足夠的記憶體來儲存字串,然後將字串複製到記憶體中。
  • 當StringBad物件過期時,str指標也將過期。但str指向的記憶體任被儲存,必須使用delete將其釋放。刪除物件可以釋放物件本身佔用的記憶體,但是不能自動釋放屬於物件成員的指標指向的記憶體。
  • 在建構函式中使用new來分配記憶體時,必須在相應解構函式中使用delete來釋放記憶體。如果使用new[](包括中括號)來分配記憶體,則應使用delete[](包括中括號)來釋放記憶體。

 

3、外部變數宣告extern

      在函式體外宣告的普通變數為外部全域性變數,可以被其他檔案使用;在函式體外宣告的靜態變數為內部全域性變數,作用域為當前原始檔。

      在函式中再次宣告一個和外部全域性變數名字相同的變數時,實際上是對該外部全域性變數進行再次宣告。可以使用關鍵字extern來重新宣告以前定義過的外部變數。

#include<iostream>
using namespace std;
double warming=0.3;

void update(double dt);
void local();

int main()
{
    cout<<"Global warming is "<<warming<<" degrees.\n";
    update(0.1);
    cout<<"Global warming is "<<warming<<" degrees.\n";
    local();
    cout<<"Global warming is "<<warming<<" degrees.\n";
    return 0;
}

void update(double dt)
{
    extern double warming;  //該變數其實就是重新聲明瞭外部宣告的全域性變數warming。關鍵字extern可以被省略
    warming+=dt;
    cout<<"Updating global warming to "<<warming;
    cout<<" degrees.\n";
}

void local()
{
    double warming=0.8;
    cout<<"Local warming = "<<warming<<" degrees.\n";
    cout<<"But gloabal warming = "<<::warming;
    cout<<" degrees.\n";
}

        要正確理解變數的初始化。初始化指的是在分配記憶體單元時給它賦值。故只能在宣告將為變數分配儲存空間時(即定義宣告),才能在宣告中初始化變數。

        extern double warming=0.5;    //INVALID。不能在引用宣告中初始化變數。


 

4、explicit關鍵字

只接受一個引數的建構函式定義了從引數型別到類型別的轉換。如果使用關鍵字explicit限定了這種建構函式,則它只能用於顯示轉換,否則也可以用於隱式轉換

//stone.h

class Stonewt
{
private:
    enum{Lbs_per_stn=14};
    int stone;
    double pds_left;
    double pounds;
public:
    Stonewt(double lbs);
    Stonewt(int stn,double lbs);
    Stonewt();
    ~Stonewt();
    void show_lbs()const;
    void show_stn()const;
};


//stone.cpp
#include<iostream>
#include"stonewt.h"
using std::cout;

Stonewt::Stonewt(double lbs)
{
    stone=int(lbs)/Lbs_per_stn;
    pds_left=int(lbs)%Lbs_per_stn+lbs-int(lbs);
    pounds=lbs;
}

Stonewt::Stonewt(int stn, double lbs)
{
    stone=stn;
    pds_left=lbs;
    pounds=stn*Lbs_per_stn+lbs;
}

Stonewt::Stonewt()
{
    stone=pounds=pds_left=0;
}

Stonewt::~Stonewt()
{

}

void Stonewt::show_stn()const
{
    cout<<stone<<" stone, "<<pds_left<<" pounds\n";
}

void Stonewt::show_lbs()const
{
    cout<<pounds<<" pounds\n";
}




//useStone.cpp
#include<iostream>
#include"stonewt.h"
using std::cout;

void display(const Stonewt st,int n);

int main()
{
    Stonewt pavarotti=260;
    Stonewt wolfe(285.7);
    Stonewt taft(21,8);

    cout<<"The tenor weighed ";
    pavarotti.show_stn();
    cout<<" The detective weighed ";
    wolfe.show_lbs();
    pavarotti=256.8;
    taft=325;
    cout<<"After dinner, the tenor weighed ";
    pavarotti.show_stn();
    cout<<"After dinner , the President weighed ";
    taft.show_lbs();
    display(taft,2);
    cout<<"The wrestler weighed even more.\n";
    display(422,2);
    cout<<"No stone left unearned \n";
    return 0;
}

void display(const Stonewt st, int n)
{
    for(int i=0;i<n;i++)
    {
        cout<<"Wow! ";
        st.show_stn();
    }
}

      C++允許指定類和基本型別之間進行轉換的方式。首先,任何接收唯一一個引數的建構函式都可被用作轉換函式,將型別與該引數相同的值轉換為類。如果將型別與該引數相同的值賦給物件,則C++將自動呼叫該建構函式。例如,假設有一個String類,它包含一個將char * 值作為唯一引數的建構函式,那麼如果bean是String物件,則可以使用下面的語句:

         bean="pinto"; //converts type char * to type String

       不過,如果在該建構函式的宣告前加上了關鍵字explicit,則該建構函式將只能用於顯式轉換:

         bean=String("pinto");//converts tyype char * to type String explicitly

 

5、this指標

      this指標時類方法可以使用的指標,它指向用於呼叫方法的物件。因此,this時物件的地址,*this是物件本身。

//mytime2.h
class Time
{
private:
    int hours;
    int minutes;
public:
    Time();
    Time(int h,int m=0);
    void AddMin(int m);
    void AddHr(int h);
    void Reset(int h=0,int m=0);
    Time operator+(const Time & s) const;
    Time operator-(const Time & s) const;
    Time operator*(double n) const;
    void Show() const;
};

//mytime2.cpp
#include<iostream>
#include"mytime2.h"

Time::Time()
{
    hours=minutes=0;
}

Time::Time(int h, int m)
{
    hours=h;
    minutes=m;
}

void Time::AddMin(int m)
{
    minutes+=m;
    hours=hours+minutes/60;
    minutes=minutes%60;
}

void Time::AddHr(int h)
{
    hours+=h;
}

void Time::Reset(int h, int m)
{
    hours=h;
    minutes=m;
}

Time Time::operator +(const Time & s) const
{
    Time sum;
    sum.minutes=minutes+s.minutes;
    sum.hours=hours+s.hours+sum.minutes/60;
    sum.minutes=sum.minutes%60;
    return sum;
}

Time Time::operator -(const Time & s) const
{
    Time diff;
    int tot1,tot2;
    tot1=s.minutes+60*s.hours;
    tot2=minutes+60*hours;
    diff.minutes=(tot2-tot1)%60;
    diff.hours=(tot2-tot1)/60;
    return diff;
}

Time Time::operator *(double mult) const
{
    Time result;
    long totalminutes=hours*mult*60+minutes*mult;
    result.hours=totalminutes/60;
    result.minutes=totalminutes&60;
    return result;
}

void Time::Show() const
{
    std::cout<<hours<<" hours, "<<minutes<<" minutes.\n";
}


//user.cpp
#include<iostream>
#include"mytime2.h"

int main()
{
    using std::cout;
    using std::endl;
    Time weeding(4,35);
    Time waxing(2,47);
    Time total;
    Time diff;
    Time adjusted;

    cout<<"weeding time= ";
    weeding.Show();
    cout<<endl;

    cout<<"waxing time= ";
    waxing.Show();
    cout<<endl;

    cout<<"total work time= ";
    total=weeding+waxing;
    total.Show();
    cout<<endl;

    diff=weeding-waxing;
    cout<<"weeding time=waxing time = ";
    diff.Show();
    cout<<endl;

    adjusted=total*1.5;

    cout<<"adjusted worked time = ";
    adjusted.Show();
    cout<<endl;

    return 0;
}

         程式中使用了操作符過載

         每個成員函式(包括建構函式和解構函式)都有一個this指標。this指標指向呼叫物件。如果方法需要引用整個呼叫物件,則可以使用表示式*this。在函式的括號後面使用const限定符將this限定為const,這樣將不能使用this來修改物件的值。