C++中的10个关键字
程序员文章站
2022-04-27 19:23:23
C++中的关键字有不少,参考了一些书籍和网站博客,抽了点儿时间,这里主要列举了10个常碰见的,可能有些老生常谈了,算是巩固复习一下吧!
1、const
const本意是&ldq...
C++中的关键字有不少,参考了一些书籍和网站博客,抽了点儿时间,这里主要列举了10个常碰见的,可能有些老生常谈了,算是巩固复习一下吧!
1、const
const本意是“不变的,常量”。
C语言:
(1)定义该变量为只读变量,分配内存【这里的常量只是编译器属性】;
(2)const与指针的组合:常量指针、指针常量、常量指针常量;
C++中:
(1)定义该变量为只读变量,在没有取地址&和加extern前和宏#define作用一样,没有内存空间【常量折叠】;
(2)类中常量需要再初始化列表里初始化,还有引用&;static const int a=10;可以直接赋值;
(3)常量对象及常量函数。常量对象只能调用常量函数且不能对成员变量进行修改;如果要修改需要加mutable关键字修饰;
(4)参数为const,防止函数对其进行修改,返回值为const不能作为左值;
(5)在C++中将作用域限定在函数或者文件中;
2、volatile
volatile本意是“易变的”。
volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。主要用于保护变量的安全。
一般说来,volatile用在如下的几个地方:
(1)、中断服务程序中修改的供其它程序检测的变量需要加volatile;
(2)、多任务环境下各任务间共享的标志,如在多线程中被几个线程共享的数据。应该加volatile;
(3)、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义。
3、static
static本意是“静态的”。
C语言:
(1)定义一个静态变量,局部的是改变了它的生命周期,全局的则是该变变它的作用域,防止同名的出现;
(2)定义一个静态函数,被定义的函数的作用域限定在当前的文件模块中;
(3)static全局变量初始化为0,且只初始化一次;
C++(唯一性):
(1)成员变量属于类,不属于任何对象,需在类外初始化;
(2)成员函数。没有this指针,所有对象共享这一个变量;
(3)static函数不能声明为虚函数;
4、mutable
mutable的本意是:“易变的,性情不定的”。
作用:当需要在const方法中修改对象的数据成员时,可以在数据成员前使用mutable关键字,防止出现编译出错。例子如下:
explict本意是“显示的”。
作用于类的内部构造函数上,防止一些隐式的转换;
例如:
A a1(12);//OK
A a2=12;//error
A a3; //error ,没有默认的构造函数;
a1=11;//error
a2=13;//error 不能做相应的隐形的转换;
6、typeid-类型标识符
标准C++的一个新特征是RTTI(Run-Time Type Information运行时类型信息),它为程序在运行时确定对象类型,提供了一种标准方法。dynamic_cast和typeid。
标准C++ 提供了typeid() 操作,以得到类型信息,它的参数可以是一个表达式,可以是一个对象、指针或者引用,通过这个方法,可以得到一个指向常type_info对象,里面包含了这个表达式的类型必要的信息,返回结果是const type_info&。不同编译器实现的type_info class各不相同。type_info对象提供的功能有如下这些:
(1)、name(),可以得到一个包含类型信息的字符串,返回如:"int"、"MyClass"等;
(2)、before(),用来在类型列表中遍历;
(3)、==操作,用来判断类型是否相同;
t1 == t2 如果两个对象t1和t2类型相同,则返回true;否则返回false
t1 != t2 如果两个对象t1和t2类型不同,则返回true;否则返回false
t.name() 返回类型的C-style字符串,类型名字用系统相关的方法产生
t1.before(t2) 返回指出t1是否出现在t2之前的bool值
主要作用:返回指针或者引用所指对象的实际类型。
如果typeid的操作数不是类类型或者是没有虚函数的类,则typeid指出该操作数的静态类型。如果操作数是定义了至少一个虚函数的类类型,则在运行时计算类型。
实例如下:
1、const
const本意是“不变的,常量”。
C语言:
(1)定义该变量为只读变量,分配内存【这里的常量只是编译器属性】;
(2)const与指针的组合:常量指针、指针常量、常量指针常量;
C++中:
(1)定义该变量为只读变量,在没有取地址&和加extern前和宏#define作用一样,没有内存空间【常量折叠】;
(2)类中常量需要再初始化列表里初始化,还有引用&;static const int a=10;可以直接赋值;
(3)常量对象及常量函数。常量对象只能调用常量函数且不能对成员变量进行修改;如果要修改需要加mutable关键字修饰;
(4)参数为const,防止函数对其进行修改,返回值为const不能作为左值;
(5)在C++中将作用域限定在函数或者文件中;
2、volatile
volatile本意是“易变的”。
volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。主要用于保护变量的安全。
一般说来,volatile用在如下的几个地方:
(1)、中断服务程序中修改的供其它程序检测的变量需要加volatile;
(2)、多任务环境下各任务间共享的标志,如在多线程中被几个线程共享的数据。应该加volatile;
(3)、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义。
3、static
static本意是“静态的”。
C语言:
(1)定义一个静态变量,局部的是改变了它的生命周期,全局的则是该变变它的作用域,防止同名的出现;
(2)定义一个静态函数,被定义的函数的作用域限定在当前的文件模块中;
(3)static全局变量初始化为0,且只初始化一次;
C++(唯一性):
(1)成员变量属于类,不属于任何对象,需在类外初始化;
(2)成员函数。没有this指针,所有对象共享这一个变量;
(3)static函数不能声明为虚函数;
4、mutable
mutable的本意是:“易变的,性情不定的”。
作用:当需要在const方法中修改对象的数据成员时,可以在数据成员前使用mutable关键字,防止出现编译出错。例子如下:
class CBook { public: mutable double m_price; // 如果不加就会出错 CBook(double price) :m_price(price) { } double getPrice() const; // 定义const方法 }; double CBook::getPrice() const { m_price = 9.8;//常量函数里修改成员变量; return m_price; }5、explicit
explict本意是“显示的”。
作用于类的内部构造函数上,防止一些隐式的转换;
例如:
class A { public: explicit A(int _size):size(_size=){} private: int size; }因为只有一个参数的构造函数是类型转换函数,可能会发生相应的隐式类型转换,加上explict就不会了!
A a1(12);//OK
A a2=12;//error
A a3; //error ,没有默认的构造函数;
a1=11;//error
a2=13;//error 不能做相应的隐形的转换;
6、typeid-类型标识符
标准C++的一个新特征是RTTI(Run-Time Type Information运行时类型信息),它为程序在运行时确定对象类型,提供了一种标准方法。dynamic_cast和typeid。
标准C++ 提供了typeid() 操作,以得到类型信息,它的参数可以是一个表达式,可以是一个对象、指针或者引用,通过这个方法,可以得到一个指向常type_info对象,里面包含了这个表达式的类型必要的信息,返回结果是const type_info&。不同编译器实现的type_info class各不相同。type_info对象提供的功能有如下这些:
(1)、name(),可以得到一个包含类型信息的字符串,返回如:"int"、"MyClass"等;
(2)、before(),用来在类型列表中遍历;
(3)、==操作,用来判断类型是否相同;
t1 == t2 如果两个对象t1和t2类型相同,则返回true;否则返回false
t1 != t2 如果两个对象t1和t2类型不同,则返回true;否则返回false
t.name() 返回类型的C-style字符串,类型名字用系统相关的方法产生
t1.before(t2) 返回指出t1是否出现在t2之前的bool值
主要作用:返回指针或者引用所指对象的实际类型。
如果typeid的操作数不是类类型或者是没有虚函数的类,则typeid指出该操作数的静态类型。如果操作数是定义了至少一个虚函数的类类型,则在运行时计算类型。
实例如下:
#include <iostream> using namespace std; class father { public: father() { } virtual void show() { cout<<"father"<<endl; class="" son:public="" father="" public:="" void="" grandson:public="" int="" p1="new" p2="new" son="" grandson="" return="" pre="">7、export 分离编译模式(Separate Compilation Model)允许在一处翻译单元(Translation Unit)中定义(define)函数、类型、类对象等,在另一处翻译单元引用它们。编译器(Compiler)处理完所有翻译单元后,链接器(Linker)接下来处理所有指向 extern 符号的引用,从而生成单一可执行文件。该模式使得 C++ 代码编写得称心而优雅。 然而该模式却驯不服模板(Template)。标准要求编译器在实例化模板时必须在上下文中可以查看到其定义实体;而反过来,在看到实例化模板之前,编译器对模板的定义体是不处理的——原因很简单,编译器怎么会预先知道 typename 实参是什么呢?因此模板的实例化与定义体必须放到同一翻译单元中。 一般是在头文件(*.h)中给出类的定义或全局函数的声明信息,而在代码文件(*.cpp)中给出具体的(类成员函数或全局函数的)函数定义。然后在多个用户代码(use.cpp)文件中包含该头文件后,就可以使用其中定义或声明的类和函数。头文件中一般不包含变量、结构和类对象的定义,因为这样可能会导致重复定义的编译错误。解决办法是,在某个代码文件中进行定义,对普通类型(包括基本数据类、结构和类),在其他用户代码文件中用关键字extern来引用它们。 但是对模板类型,则可以在头文件中,声明模板类和模板函数;在代码文件中,使用关键字export来定义具体的模板类对象和模板函数;然后在其他用户代码文件中,包含声明头文件后,就可以使用该这些对象和函数了。 例如: 普通内置类型:
extern int n; extern struct Point p; extern class A a;模板类型:
export template class Stack s; export template void f (T& t) {……}例如: // out.h:(声明头文件——只包含out函数的声明信息)
template void out (const T& t);// out.cpp:(定义代码文件——包含out函数的声明[通过include]和定义等全部信息)
#include #include “out.h” export template void out (const T& t) {std::cerr//user.cpp:(用户代码文件——包含函数的声明头文件后就可以使用该函数)
#include “out.h”补充:在linux下的export: 主要的作用是将局部变量-SET转换为环境变量-ENV。 export 也是 bash 的一个内置命令。它主要是用来将父 shell 里的变量导出供子 shell 使用。登录主机后,在执行Bash Script之前,其实我们已经处于一个BashShell中。这个Shell叫login Shell,是将来我们执行任何Script的上层环境,又叫父SHell。父Shell会根据Script程序的第一行#!之后指定的Shell程序开启一个子Shell环境,然后在子Shell中执行此 Shell Script。一旦子Shell中的Script执行完毕,此子Shell随即结束,回到父Shell中,不会影响父Shell原本的环境。子Shell环境拥有与父Shell相同的环境变量、标准输入、输出、错误等。 这 里注意上面说的是环境变量而不是局部变量,如果是局部变量,在子shell中又要使用,这时候export就派上用场了! 作用如下: 1. 用 export 导出的变量放在“导出变量列表”中,它可以被子 shell (子 shell 的子 shell 也是如此)拷贝并使用。 2. 被 export 出来的变量虽然可以被子 shell 使用,但它也只是一个拷贝,而不会影响到父 shell 中的值以及其它子 shell 中的值。 8、asm 作用:程序代码内嵌汇编
#include "stdafx.h" void main() { int var = 1; int temp = var; printf("var=%d\n",var); __asm { mov dword ptr [ebp-4], 10h } int ret = var; printf("var= %d\n",ret); }mov dword ptr [ebp-4], 10h 的含义如下: mov是传送指令 把十六进制的10(也就是十进制的16)传送给dword ptr [ebp-4]~如果用的是vc6的话~设断点->调试->alt+8可以看到ptr [ebp-4]其实是var的地址~也就是改变了var的值~ 9、#define和typedef的区别 #define:是宏定义,宏定义就是单纯的替换,把钱替换成面包,把黑板擦替换成抹布,已经不是原来的东西了。 (1)在编译期间起作用,没有内存空间,编译期间宏的名称没有进入符号表,这也是尽量用const和inline替换#define的原因之一; (2)没有数据类型; typedef:别名,彻底的封装,不容改变。指向的是同一个空间,名字不同罢了! 例如:一个人有中文名,有小名,有外国名等等,但都是一个人! 区别: (1)#define能用其他的类型进行扩展,但是typedef不行
#define INT int typedef int TYPEINT; unsigned INT num; //OK但是
unsigned TYPEINT num;//ERROR; //彻底的封装,不容改变(2)typedef能保证变量类型的一致,但是#define不行
#defined PINT int * typedef int* pINT; PINT a,b;//等价于:int *a,b; 也就是int *a; int b; a、b的类型不一致; pINT a,b;//等价于:int *a; int *b; a、b的类型一致;10、inline 内联函数: 注意:inline只是程序员对编译器的建议,有inline不一定是inline函数,但是没有inline一定不是inline函数。 许多书上都会提到这是因为编译器比绝大多数程序员都更清楚函数调用的开销有多大,所以如果编译器认为调用某函数的开销相对该函数本身的开销而言微不足道或者不足以为之承担代码膨胀的后果则没必要内联该函数。内联使用不恰当是会有副作用的:会带来代码膨胀。内联函数本来是希望用稍微的代码膨胀换取时间效率的,可是现在代码膨胀了,效率一样没提高!! 内联函数和宏很类似,而本质区别在于: 宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的,直接将响应的函数代码替换函数调用。而且内联函数是真正的函数(进行类型检查),只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,跳转(来回跳跃并记录跳跃位置),减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。
推荐阅读