C/C++关键字总结学习

39 篇文章 14 订阅

#define

宏定义,预处理机制

sizeof

以字节读取运算对象的大小
在C中,一字节定义为char类型占用空间的大小,运算对象可以是具体的数据对象(如变量名)或类型。如果运算对象是类型,必须要用括号括起来。
sizeof返回的是size_t类型的值,这是个无符号整数类型

typedef unsigned int size_t;

C99标准规定,函数、不能确定类型的表达式以及位域(bit-field)成员不能被计算sizeof值
下面是错误的:

sizeof(foo);//error
void foo2(){}
sizeof(foo2());//error
struct S
{
unsigned int f1:1;
unsigned int f2:5;
unsigned int f3:12;
};
sizeof(S.f1);//error

举例:

	int a = 666;
	printf("%d\n%u",sizeof a,sizeof(int));//结果都为4

sizeof的计算发生在编译时刻,所以它可以被当作常量表达式使用,如:

char ary[sizeof(int)*10];//ok

const

用于限定一个变量为只读,比#define的用法更灵活,可以创建const数组,const指针和指向const的指针。

1,在指针中使用const

要区分是限定指针本身为const,还是限定指针指向的值为const。

  • 常量指针:指向常量的指针,不能更改指向的值:
const float * pf;//pf指向一个float类型的cosnt值
float const * pf;//同上
//pf指向的值不能改变,但是pf本身的值可以改变,例如可以设置该指针指向其他const值。
//也就是说,pf指向的地址可以更改,但是它地址上的值要为const类型
  • 指针常量:为指针类型的常量,创建的指针本身为const:
float * const pt;//pt是一个const指针
//指针本身的值不能更改,pt必须指向同一个地址,但是地址上的值可以更改,
  • 指针本身为const类型,指向的值也为const类型;
const float * const ptr;//表明ptr既不能指向别处,它所指向的值也不能改变

总结,const在 * 号左边,表明限定了指针指向的数据不能改变;const在 * 号右边,限定了指针本身不能更改。

2,在函数参数中使用const

void display(cosnt int array[], int limit);//const int array[]和const int * array相同

数组名是个地址,函数有可能会更改数组的数据,加了const关键字之后,使函数只能读取array数组的值,而不能修改它。
如果一个指针仅用于给函数访问值,应将其声明为const限定类型指针,如果要用指针更改主调函数中的数据,则不使用const关键字。
尽可能使用const:
- 可以避免由于无意间修改数据而导致的编程错误
- 使用const使得函数能够处理const和非const实参,否则只能接受非const数据。

3,对全局数据使用const

使用全局变量是一种冒险的方法,因为暴露了数据,程序的任何一部分都能更改数据。如果把数据设置为const,就可以避免这样的危险。
然而,在文件间共享const数据要小心,可以采用两个策略:第一,遵循外部变量的常用规则,即在一个文件中使用定义式声明,在其他文件中使用引用式声明(用extern关键字)。第二种方案是把const变量放在一个头文件中,然后在其他文件中包含该头文件。
设置常量,在C语言中用#define来设置常量,在C++中通常使用const在程序开头来设置常量。

4, 对类中成员函数使用const—常成员函数

常成员函数是指由const修饰符修饰的成员函数,在常成员函数中不得修改类中的任何数据成员的值
1.常成员函数不能更新对象的数据成员
2.当一个对象被声明为常对象,则不能通过该对象调用该类中的非const成员函数

  • 为了使成员函数的意义更加清楚,我们可在不改变对象的成员函数的函数原型中加上const说明
  • const成员函数应该在函数原型说明和函数定义中都增加const限定
  • 非常量成员函数不能被常量成员对象调用,因为它可能企图修改常量的数据成员

为什么需要const成员函数?

  1. 我们定义的类的成员函数中,常常有一些成员函数不改变类的数据成员,也就是说,这些函数是"只读"函数,而有一些函数要修改类数据成员的值。如果把不改变数据成员的函数都加上const关键字进行标识,显然,可提高程序的可读性。其实,它还能提高程序的可靠性,已定义成const的成员函数,一旦企图修改数据成员的值,则编译器按错误处理
  2. 为了确保const对象的数据成员不会被改变,在C++中,const对象只能调用const成员函数。如果一个成员函数实际上没有对数据成员作任何形式的修改,但是它没有被const关键字限定的,也不能被常量对象调用。

const修饰成员函数相当于重载

class A
{
public:
	A() {
		a = 0;
	}
	int a;
	void test()const {
		cout << "const 成员函数 a = "<< a << endl;
	}
	void test() {
		cout << "普通成员函数 ++a = "<< ++a << endl;
	}
};
int main(void) {
	A a;
	a.test();//调用普通成员函数test
	const A a1;
	a1.test();//调用const成员函数
}

auto

默认情况下,声明在块或者行数投中的任何变量都属于自动存储类别。关键字auto是存储类别说明符,将变量声明为自动变量,具有自动存储期、块作用域且无链接。auto关键字在C++中完全不同,如果是编写C/C++兼容程序,最好不要使用auto。

register

  • 声明寄存器变量,可声明为寄存器变量的数据类型有限,例如处理器中的寄存器可能没有足够大的空间来储存double类型的值。
  • 寄存器变量储存在CPU的寄存器中,就是储存在最快的可用内存中,与普通变量相比,访问和处理这些变量的速度更快。
  • 由于寄存器变量储存在寄存器中而非内存中,所以无法获取寄存器变量的地址。

static

静态变量的意思是:该变量在内存中的地址不变,并不是说它的值不变。

  • 静态变量和自动变量一样,具有块作用域、无链接,但是具有静态存储期,当程序离开它们所在的函数后,静态变量不会消失,
  • 不能在函数形参中使用static,如
int test(static int flu );//不允许

使用局部静态变量

//使用局部静态变量
#include<stdio.h>
void trystat(void);

int main(void){
	int count ;
	for (count = 1; count < 5; count++){
		printf("Here comes iteration %d:\n",count);
		trystat();
	}
	
	return 0;
} 

void trystat(void){
	int fade = 1;
	static int stay1 ;
	static int stay2 = 1 ;
	printf("fade = %d    stay1 = %d     stay2 = %d\n", fade++, stay1++, stay2++);
}
/*
结果: 
	Here comes iteration 1:
	fade = 1    stay1 = 0     stay2 = 1
	Here comes iteration 2:
	fade = 1    stay1 = 1     stay2 = 2
	Here comes iteration 3:
	fade = 1    stay1 = 2     stay2 = 3
	Here comes iteration 4:
	fade = 1    stay1 = 3     stay2 = 4
分析:
每次调用trystat()都会初始化fade,但是stay2只在编译trystat()是被初始化一次,
如果为显式初始化静态变量,如stay1,它会被默认初始化为0 
*/

静态类成员变量

特点:无论创建了多少个对象,程序都只创建一个静态类变量副本。也就是说类的所有对象共享同一个静态成员。
静态数据成员注意事项:

  • 在类中声明
  • 在包含类方法的文件中初始化
  • 初始化时使用作用域运算符来指出静态成员所属的类
  • 如果静态成员是const整数类型或枚举类型,则可以在类声明中初始化
class Test
{
public:
    Test();
    static int nums;
};
Test::Test()
{
    cout<<nums++<<endl;
}
int Test::nums = 0;
int main(int argc, char *argv[])
{
    Test aa,bb,cb;
    return 0;
}
结果为:
0
1
2

静态类成员函数

可以将成员函数声明为静态的(函数声明必须包含关键字static,但如果函数定义是独立的,则其中不能包含static关键字)
注意事项:

  • 不能通过对象来调用静态函数,实际上静态成员函数甚至不能使用this指针,如果静态成员函数是在公有部分声明的,则可以使用类名和作用域解析运算符来调用它
  • 静态成员不与特定的对象关联,因此只能使用静态数据成员,不能使用非静态数据成员,因为它们可能还没初始化。
class Test
{
public:
    Test();
   //类中声明
   static int print(){ cout<<"hello";}
};
Test::Test()
{
    cout<<nums++<<endl;
}
int Test::nums = 0;
int main(int argc, char *argv[])
{
    Test::print();
    return 0;
}

extern

外部链接的静态变量具有文件作用域、外部链接和静态存储期。该类别优势成为外部存储类别,属于该类别的变量称为外部变量。把变量的定义性声明放在在所有函数的外面便创建了外部变量。如果一个源代码文件使用的外部变量定义在另一个源代码文件中,则必须用extern关键字在该文件中声明该变量。

//在函数外
double up; //定义,全局变量,默认为0
extern int b;//声明,告诉编译器该变量在别处定义
extern int c=2;//定义,因为初始化了

如果要在多个文件中使用外部变量,只需在一个文件中包含该变量的定义(单定义规则),但在使用该变量的其他所有文件中,都必须使用extern关键字声明它;

//file01.cpp
extern int a = 22;//定义,可以省略extern
int b = 33;//定义
int c;//定义
//file02.cpp
extern int a;//声明
extern int b ;//声明
//file03.cpp
extern int a;//声明
extern int b ;//声明
extern int c ;//声明

同时编译这三个文件,可以在file02中使用变量a和b,可以在file03中使用a,b,c.

volatile

volatile限定符告知计算机,代理(而不是变量所在的程序)可以改变该变量的值。通常它被用于硬件地址以及在其他程序或同时运行的线程中共享数据。
要求编译器不要对其描述的对象作优化处理,对它的读写都需要从内存中访问。
智能的(进行优化的)编译器可能会把变量的值临时储存在寄存器上,便于下次读取,以节约时间,这个过程被称为高速缓存。但是有一些代理在内存上改变了变量的值,寄存器上的还是旧数据,这样就出错了。如果被volatile 关键字修饰,编译器不会进行高速缓存,直接去内存中读取该变量的数据。
编译器优化什么

  1. 将内存变量缓存到寄存器中
  2. 调整指令顺序,充分利用CPU指令流水线,进行指令重新排序读写指令

restrict

restrict关键字允许编译器优化某部分代码,以更好地支持计算。它只能用于指针,表明该指针是访问数据的唯一方式。

int * restrict restar = (int *) malloc(10 * sizeof(int));

这里表明,指针restar是访问有malloc()多分配内存的唯一方式。restrict有两个读者:一个是编译器,告诉编译器可以自由假定一些优化方案。另一个读者是用户,告知用户要使用满足restrict要求的参数。

typedef

typedef工具是一个高级数据特性,利用typedef可以为某一类型自定义名称,这方面与#define类似。但是二者有三处不同:

  • 与#define不同,typedef创建的符号名之受限于类型,不能用于值。
  • typedef由编译器解释,不是预处理器
  • 在其受限范围内,typedef比#define更灵活。

typedef并没有创建任何新类型,它只是为某个已存在的类型增加了一个方便使用的的标签。

C++函数修饰符

  • const 修饰成员函数,该函数不能修改类的任意数据成员,也不能调用类的其他成员函数,除了那些也被声明为const的函数。cosnt放在函数后。
  • constexpr,C++11特有的,编译器将把从函数的返回值十位编译时的常量。此函数必须正好有一个return语句。
  • explicit,只修饰成员函数(具体来说是构造函数),使构造函数不提供隐式转换的功能。
  • final,C++特有的,只修饰成员函数,适用于虚函数,防止该函数被派生类重载(该关键字通常运用于派生类内的虚函数,此派生类可能有自己的派生类,final的要点是防止对函数有进一步的重载)。
  • inline,内联函数,修饰任意函数,此关键字是一个建议,他告诉编译器应该把此函数实现为内联函数。最先进的现代编译器是优化的编译器,它们自己决定要内联的对象,所以一度被认为很重要的关键字,现在只是一个对编译器的建议。
    特点:以空间换时间,提高函数调用的效率。C++ 内联函数是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。对于短函数进行内联是非常有效的,这消除了函数调用的开销。递归函数不能被内联
  • override,C++11特有的,只修饰成员函数,该关键字指定该函数重载基类中声明的一个函数。
  • static,当其应用于全局函数时,可使函数只在当前源文件中可见。在默认情况下,C++中的函数名具有外部链接,使每个模块用相同的名称声明的一个函数必须指向相同的函数(它必须只在一个模块中被定义)。当用于成员函数时,该函数变成类的一个静态函数,使得它不能访问类的其它 成员,除非它们也被声明为静态。通过 类名::函数 语法来调用。

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;
};

上述四种构造函数:

  • 构造函数1没有参数,无法进行类型转换!
  • 构造函数2有一个参数,可以进行类型转换,如:Demo test; test = 12.2;这样的调用就相当于把12.2隐式转换为Demo类型。
  • 构造函数3有两个参数,且无默认值,故无法使用类型转换!
  • 构造函数4有3个参数,其中两个参数有默认值,故可以进行隐式转换,如:Demo test;test = 10;
1 /* 示例代码2 */
 2 class Demo
 3 {
 4    public:
 5     Demo();                     /* 构造函数1 */
 6     explicit Demo(double a);        /* 示例代码2 */
 7     Demo(int a,double b);           /* 示例代码3 */
 8
 9     ~Demo();
10     void Func(void);
11
12     private:
13     int value1;
14     int value2;
15 };

在上述构造函数2中,由于使用了explicit关键字,则无法进行隐式转换。即:Demo test;test = 12.2;是无效的!但是我们可以进行显示类型转换,如:

Demo test;
test = Demo(12.2); 
test = (Demo)12.2;

使用explicit防止单参构造函数的隐式转换,使用const限制方法修改数据,这样做的的根本原因是:在编译阶段出现错误优于在运行阶段出错。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SOC罗三炮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值