欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++关键字解析

程序员文章站 2022-07-13 23:35:46
...

const关键字

  • 修饰变量 说明变量不可变
  • 修饰指针 分为指向常量的指针与指针常量 int * const p 与 const int * p
  • 修饰引用 常量的引用:经常用于形参,既避免了拷贝,又能避免函数对值的修改 没有引用常量
  • 修饰成员函数

const成员函数不能修改成员变量
const对象只能调用const成员函数,非const对象可以访问任意的成员函数,包括const成员函数腿甲const对象的成员是不可修改的,但是对象通过指针维护的对象是可修改的。

const修饰成员函数时,准确说是修饰this指向的对象。
非const函数中,this是一个指针常量,指向对象,而const对象中,this是一个指向常量对象的指针常量。

  • 修饰函数返回值 函数返回值指向的内容是常量,返回值只能赋值给const修饰的同类型的变量。
  1. 返回值通过指针传递 返回值只能赋值给const修饰的同类型指针
  2. 返回值通过值传递,函数会将值复制到外部临时存储单元,加const没有价值。
    这种情况可以将函数返回值改为const引用 例如 A getA(void) 写成 const A & getA(void)
    此时一定要注意函数是返回对象的copy还是仅仅返回对象的别名
  3. 连续赋值时 常常使用 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关键字

  1. 声明与定义中
    C++支持分离式编译,该机制允许将程序分割为若干文件,每个文件单独编译。有些文件中需要共享变量或者函数之类,为了支持这种特性,C++允许将声明与定义分开。一个文件如果想要使用别处定义的名字,必须包含对那个名字的声明。
    如果只想要声明,而非定义,在变量名之前添加extern关键字,而非显式地初始化。
extern int i; // 声明i而非定义
int j; //声明并定义
extern int k = 1; //声明并定义
  1. extern关键字主要修饰变量或者函数,表示函数可以跨文件使用,或者表明变量在其他文件定义,在此处引用。除此之外,extern还可以用来进行链接指定

声明函数或者全局变量的作用范围的关键字,声明的函数或者变量可以在本模板以及其他模板中使用。记住是声明
链接指定。C++支持重载,因此编译器会将函数名+参数列表结合生成一个中间函数名进行区分。如果想使用C函数,那么可能出现找不到对应的函数定义的问题。使用extern “C” void fun(int a)告诉编译器按照C的规则翻译函数名,而不是C++的。

  1. 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. 指定简单的别名,避免了书写过长的类型名称
  2. 实现一种定长的类型,跨平台编程使用
  3. 使用一种方便阅读的单词作为别名 方便阅读代码
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优点:

  1. 方便程序的修改 宏定义一个程序中经常使用的常量
  2. 提高程序运行效率 函数使用时需要保留现场,便于子函数执行结束后返回,然后恢复现场。但对于只进行一点操作子函数,这个转换开销很大,使用带有参数的宏定义不会出现转换,直接进行代码展开。