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来修改对象的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值