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來修改物件的值。