【C++】深入理解C++的常见关键字

标签: c++
244人阅读 评论(0) 收藏 举报
分类:

1、auto

void foo() { auto int f = 0; }
void foo() { int f = 0; }

auto变量即自动变量或局部变量,位于一个进程的地址空间的堆栈段,它们是局限于一个函数的,出现在一个作用域内,进入作用域时自动生成,离开作用域时自动消失,这种变量默认就是auto。

2、register

for (register int i = 0; i < 100; ++i) {}

register变量即寄存器变量,是一种局部变量,它告诉编译器尽可能快地访问这个变量,加快访问速度取决于实现,经常是通过在寄存器中放置变量来做到的,但这只是对编译器的一个暗示,不能保证将变量放置在寄存器中,也不能保证提高访问速度。使用register变量是有限制的,不可能得到或计算register变量的地址,register变量只能在一个块中声明,不可能有全局的或静态的register变量,然而可以在一个函数中即在参数表中使用register变量作为一个形式参数。一般地,不应当推测编译器的优化器,因为它可能比我们做的更好,因此,最好避免使用关键字register。

3、extern

extern int global;
extern void foo();
void foo();
extern "C" void foo();
extern "C" void foo() {}
extern "C++" void foo();
extern "C++" void foo() {}

extern是个存储类型说明符。如果在一个文件中使用extern关键字来声明另一个文件中存在的全局变量,那么这个文件可以使用这个数据,意思是这个变量在别处定义,在这里可以使用。extern用于函数时表示这个函数在别处定义,在这里可以使用,头文件中的函数声明默认使用了关键字extern。另外,为了保证CC++的兼容性,在函数声明和定义时,还需要使用extern "C"extern "C++"来说明这个函数是C函数还是C++函数。

4、static

void foo() { static int f = 100; ++f; }
static int foo = 100;
static void foo() {}

class A { static int i; };
int A::i = 100;

static是个存储类型说明符,有两层含义,一是生命期,而是作用域。static用于函数内的局部变量时,这个局部变量当然是函数内作用域,但它的生命期直到程序退出时才结束,只初始化一次,随后的每次函数访问中都保持上次的值。static用于全局变量即函数外的变量时限制了变量的作用域为文件内作用域,文件外即使使用extern也不能访问这个变量,同理static用于函数时限制了函数的作用域为文件内作用域,文件外即使使用extern也不能访问这个函数。static关键字的深层含义是为每个标识符创建单独的存储空间,别的文件可以使用相同的标识符而不会发生冲突,这称为内部连接。还有一种连接方式是外部连接,外部连接为所有被编译过的文件创建一片共同的存储空间,两个文件不可以使用相同的标识符进行定义,全局变量(函数之外定义的所有变量, 在C++中除了const,C++中const默认内部连接,C中const默认外部连接,可参照下面的const说明)和函数定义默认为外部连接,通过用关键字extern声明,可以从其它文件访问这些变量和函数。局部变量临时存储在堆栈中,即不是内部连接,也不是外部连接。
C++中,全局静态对象的构造函数是在main函数之前调用的。类的静态成员变量有着单一的存储空间而不管产生了多少个对象,必须有定义且定义必须出现在类的外部,所以嵌套类中可以有静态成员变量,而函数内的局部类中不可以有静态成员变量。静态成员函数因为没有this指针,所有不能访问非静态的成员变量,也不能调用非静态的成员函数。

5、const

#define PI 3.14
const int x = 10;
extern const int x;
char y[x];

const int i[] = {1, 2, 3, 4};
float f[i[3]]; // error

定义常量可以使用宏定义,但这种常量发生在预处理阶段,直接进行替换,不占用存储空间,属于文件内作用域,不作类型检查,也不能取得地址。C++引入了const关键字,const变量表示这个变量只读,不能修改,后来加入到标准C,但两者很不一样。当在C中定义const时,编译器为它创建存储空间,也就是上面static说明中提到的外部连接,所以如果在两个不同的文件中定义多个同名的const,连接器将生成发生冲突的错误消息,同时在编译期间也不能使用它的值,但是在C++中属于内部连接,编译器并不为const创建存储空间,相反它把这个定义保存在它的符号表里,就没有这样的错误。在C++中,一个const必须有初始值,除非用extern作出了清楚的说明,还包括取const的地址,这时const就发生了变化,因为extern意味着外部连接,因此必须分配存储空间,让其它的编译单元可以引用,在C中不是这样。编译器在编译时可以通过必要的计算把一个复杂的常量表达式通过缩减简单化,这一点在上面的数组定义里显得尤为重要,这称为常量折叠。const可以用于聚合,如上面的数组,这种情况下编译器不会把它保存到符号表中,而是分配内存,这样的话,在编译期间不能使用它的值,因为编译器在编译器期间不需要知道存储的内容,也可以理解为是个运行时常量,而非编译时常量。上面的描述适用于const变量,同样适用于const指针。

class A { const int i; A() : i(100) {} };
class A { static const int i = 100; };
class A { enum {i = 100}; };
class A { void foo() const {} };

类中的const成员变量同C中的const,要分配存储空间并进行初始化,但初始化工作是在初始化列表中完成的,那么如何在类里使用一个编译期间的常量呢,同时使用static和const即可,并且在定义的地方进行初始化,另外一种方法是使用enum,enum在编译期间也是知道值的。const成员函数表示这个函数不能修改类中的成员变量,const关键字在函数声明和定义时都必须有,const成员函数可以使用const成员变量和非const成员变量,可以调用const成员函数,但不能调用非const成员函数。其实在const成员函数中修改成员变量也是有法可依的,一种方法是把this指针转换为非const的对象指针,另一种方法是给要修改的成员变量添加关键字mutable。

6、volatile

volatile int x = 10;

volatile的语法与const是一样的,但是volatile的意思是在编译器认识的范围外这个数据可以被改变,不知何故,环境正在改变数据,可能通过多任务、多线程或者中断处理,所以,volatile告诉编译器不要擅自作出该数据的任何假定,优化期间尤其如此。就像建立const对象一样,程序员也可以建立volatile对象,甚至还可以建立const volatile对象,这个对象不能被客户程序员改变,但可通过外部的代理程序改变。把const和volatile连在一起称为c-v限定词,volatile像const一样,可以对成员变量、成员函数和对象本身使用。

7、mutable

class A { mutable int i; };

类中的mutable成员变量表示可以在const成员函数中进行修改。

8、inline

#define PLUSONE(x) (++x)
inline int plusOne(int x) { return ++x; }

C中,函数宏为效率而生,没有普通的函数调用代价,用预处理器而不是编译器,直接用宏代码代替宏调用,没有参数压栈、生成汇编预言的CALL、返回参数、执行汇编语言的RETURN等,但容易隐藏难以发现的错误,且不允许访问类的成员函数,为了既保持预处理器宏的效率,又增加安全性,而且还能像一般成员函数一样可以在类里访问自如,C++引入了内联函数,使用关键字inline。
任何在类中定义的函数自动地成为内联函数,或者在类外定义成员函数时使用inline,也可以在非类的函数定义前面加上inline关键字使之成为内联函数。在类声明结束后,其中的内联函数才会被计算,所以一个内联函数在类中向前引用一个还没有声明的函数是可以的。一般应该把内联定义放在头文件里,内联函数处于一种特殊状态,不会出现产生多个定义错误的情况,当编译器看到这个定义时,它把函数类型(函数名和返回值)和函数体放到符号表里,当使用函数时,编译器检查以确保调用是正确的且返回值被正确使用,然后将函数调用替换为函数体,因而消除了开销。内联代码的确占用空间,但假如函数较小,这实际上比为了一个普通函数调用而产生的代码(参数压栈和执行CALL)占用的空间还小。但是,假如函数较大,由于需要在调用函数的每一处重复复制代码,这样将使代码膨胀,在速度方面获得的好处就会减少。内联仅是编译器的一个建议,如果函数太复杂,或者要显式或隐式地取函数地址,编译器将不能执行内联。

9、cast

a_type var = static_cast<a_type>(a_var);
a_type var = const_cast<a_type>(a_var);
a_type var = reinterpret_cast<a_type>(a_var);
a_type var = dynamic_cast<a_type>(a_var);

static_cast用于明确定义的转换,包括编译器允许我们所做的不用强制转换的安全转换和不太安全但清楚定义的转换,具体包括典型的非强制转换、有丢失信息的窄化转换、使用void*的强制转换、隐式类型转换和类层次的静态定位。const_cast用于将const转换为非const,volatile转换为非volatile,如果进行别的转换,可能会得到编译错误。reinterpret_cast是一种最不安全的转换机制,可以转换成完全不同的类型,但最终要转换成它原来的类型。dynamic_cast用于类型安全的向下转换。

1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:325896次
    • 积分:6053
    • 等级:
    • 排名:第4705名
    • 原创:257篇
    • 转载:0篇
    • 译文:5篇
    • 评论:45条
    Make Others Better.
    博客专栏
    文章分类