C++关键字解析
const关键字
- 修饰变量 说明变量不可变
- 修饰指针 分为指向常量的指针与指针常量 int * const p 与 const int * p
- 修饰引用 常量的引用:经常用于形参,既避免了拷贝,又能避免函数对值的修改 没有引用常量
- 修饰成员函数
const成员函数不能修改成员变量
const对象只能调用const成员函数,非const对象可以访问任意的成员函数,包括const成员函数腿甲const对象的成员是不可修改的,但是对象通过指针维护的对象是可修改的。
const修饰成员函数时,准确说是修饰this指向的对象。
非const函数中,this是一个指针常量,指向对象,而const对象中,this是一个指向常量对象的指针常量。
- 修饰函数返回值 函数返回值指向的内容是常量,返回值只能赋值给const修饰的同类型的变量。
- 返回值通过指针传递 返回值只能赋值给const修饰的同类型指针
- 返回值通过值传递,函数会将值复制到外部临时存储单元,加const没有价值。
这种情况可以将函数返回值改为const引用 例如 A getA(void) 写成 const A & getA(void)
此时一定要注意函数是返回对象的copy还是仅仅返回对象的别名- 连续赋值时 常常使用 A & operator = (const A a); 这样是为了支持连续赋值。
A = B = C 先运行 B=C 返回B 然后运行 A=B。有一种情况 (A=B) = C,也是合理的,但是如果重载时加上const就不行,A不能二次赋值。
推荐将const修饰的变量写在const所修饰的变量前面。
const int i=10;
int const i = 10;(推荐写法)
int * const pi = &i;// 指向int的const指针
int const *pi = &i;// 指向const int的指针
const int *pi = &i;
static关键字
-
修饰普通变量 修改变量存储区域与生命周期。(静态数据区的变量会在程序刚开始时进行初始化,没有初始值就用初始值系统默认初始值进行初始化,这也是唯一一次初始化。而全局变量与静态变量就存放在静态存储区。)
修饰全局变量: 限制使用范围。全局静态变量在声明文件之外不可见。默认情况下,所有未加static前缀的全局变量与函数具有全局可见性。加上static之后可以预防不同文件的命名冲突。
修饰局部变量: 改变局部变量生存周期与存储方式,但是不改变作用域。在退出局部变量所在函数之后,变量继续存在,但是不能访问,直到下次访问该函数。 -
修饰普通函数: 只能在定义它的源文件中使用, 不能在其他源文件中使用
-
修饰类成员变量与成员函数: 所有对象共享一套静态成员,静态成员通过 类名::变量名访问,静态函数内不能访问非静态成员。
extern关键字
- 声明与定义中
C++支持分离式编译,该机制允许将程序分割为若干文件,每个文件单独编译。有些文件中需要共享变量或者函数之类,为了支持这种特性,C++允许将声明与定义分开。一个文件如果想要使用别处定义的名字,必须包含对那个名字的声明。
如果只想要声明,而非定义,在变量名之前添加extern关键字,而非显式地初始化。
extern int i; // 声明i而非定义
int j; //声明并定义
extern int k = 1; //声明并定义
- extern关键字主要修饰变量或者函数,表示函数可以跨文件使用,或者表明变量在其他文件定义,在此处引用。除此之外,extern还可以用来进行链接指定
声明函数或者全局变量的作用范围的关键字,声明的函数或者变量可以在本模板以及其他模板中使用。记住是声明
链接指定。C++支持重载,因此编译器会将函数名+参数列表结合生成一个中间函数名进行区分。如果想使用C函数,那么可能出现找不到对应的函数定义的问题。使用extern “C” void fun(int a)告诉编译器按照C的规则翻译函数名,而不是C++的。
- extern一般的使用规则如下: 在.h文件中使用extern声明变量或者函数,在.c文件中进行定义,然后其他文件中使用的时候直接使用extern + 变量名/函数名。相比于include的方式,extern更加轻便,但是如果变量函数名很多,最好还是使用include。
volatile关键字
- 不可优化性 一种类型修饰符,用它声明的类型变量不能被某些编译器位置因素更改,告诉编译器不对这样的对象进行优化。
- 易变性 每次访问必须从内存中取值(没被volatile修饰的变量可能会被编译器优化,从CPU寄存器取值)
explicit关键字
防止类构造函数的隐式自动转换,使用explicit关键字修饰的构造函数不能自动进行隐式转换,只能显式转换。
注意:只有一个参数的构造函数,或者n个参数,n-1个参数有默认值的情况才能有隐式自动转换。
/* 示例代码1 */
class Demo
{
public:
Demo(); /* 构造函数1 */
Demo(double a); /* 示例代码2 */
Demo(int a,double b); /* 示例代码3 */
Demo(int a,int b=10,double c=1.6); /* 示例代码4 */
~Demo();
void Func(void);
private:
int value1;
int value2;
};
构造函数一无参数,不能进行自动转换。二有一个参数,可以。Demo test = 12.2;三有两个参数,不行。四只有一个没有默认值的参数,可以。Demo test =12.2;
explicit Demo(double a); /* 示例代码2 */
Demo(int a,double b); /* 示例代码3 */
explicit Demo(int a,int b=10,double c=1.6); /* 示例代码4 */
使用explicit关键字可以防止这种隐式转换。
typedef 关键字
用于定义一种数据类型的别名。typedef并没有创建新的类型,只是建立了一个类型的别名而已。typedef定义的数据类型的作用域只在该语句的作用域之内。
1.语法与使用场景
- 指定简单的别名,避免了书写过长的类型名称
- 实现一种定长的类型,跨平台编程使用
- 使用一种方便阅读的单词作为别名 方便阅读代码
1.1 减少书写类型长度
C语言定义结构体方式如下:
// 方式一
struct Mystruct{
int data;
};
struct Mystruct a,b;
// 方法二
struct Mystruct{
int data;
}a,b;
// 方法三
struct {
int data
}a,b;
使用typedef:
// 方式一
struct Mystruct{
int data;
};
typedef struct Mystruct newtype;
newtype a;
// 方法二
typedef struct Mystruct{
int data;
}newtype;
newtype a,b;
// 方法三
typedef struct {
int data
}newtype;
newtype a;
1.2 实现一种定长的类型
c/c++并没有规定int类型长度,可能会出现不同机器上对int类型变量所占用的字节数不同。为了解决这种问题,可以定义一种固定大小的类型 比如 Int32,用于表示固定长度的整形。
1.3 实现一种有意义可读的别名
可以自定义一种名称 代替已有类型,便于代码阅读
typedef int km_per_hour
但是下面这种用法不行
unsigned int a;
unsigned km_per_hour b;
2. 其他情况下的使用
2.1 定义指针
typedef char * CHARS;
typedef CHARS const CPTR1
typedef const CHARS CPTR2
注意这里CPTR1,CPTR2都表示只想char数组的常量,尤其是CPTR2,不仅仅将typedef看成是代码替换,而是将CHARS看成一个整体。
2.2 定义数组
typedef char arrType[6];
这种方式比较特别 [size]在新数组名之后
C++11中推荐使用using代替typedef
C++11扩展了using的场景,可以使用using定义类型别名
using 别名 = 类型;
define
#define是C语言提供的宏命令,一般作用就是一个文本替换工具,预处理器完成,无脑替换即可。
用于将一个标识符定义为一个字符串,标识符称为宏名,被定义的字符串称为替换文本。一般有两种格式:一种简单的宏定义,另一种是带参数的宏定义。
1. #define <宏名> <字符串>
#define PI 3.1415926
2. 带参数的宏定义
#define <宏名> (<参数表>) <宏体>
#define A(x) x
一个标识符被宏定义后,标识符就是一个宏名,在程序的预编译阶段,将宏名用被定义的字符串替换,这叫做宏替换。
#define A(x) x+x
cout << A(2)A(2) << end; 结果为 2+22+2 = 8;
因此为了不引起歧义,字符串中不只一个符号,加上括号表现优先级。如果是带参数的宏定义,要给每个参数加上括号,最后给i结果加上一个括号。#define A(x) ((x) * (x)) 这样 A(2+2)/A(2+2)才能正确计算。
#define优点:
- 方便程序的修改 宏定义一个程序中经常使用的常量
- 提高程序运行效率 函数使用时需要保留现场,便于子函数执行结束后返回,然后恢复现场。但对于只进行一点操作子函数,这个转换开销很大,使用带有参数的宏定义不会出现转换,直接进行代码展开。
上一篇: 去除有序数组/链表的重复元素
下一篇: 空指针