C++:00.一些注意点
以前记录的一些注意点,暂时先放在这里
1、使用静态类型的编程语言是在编译时执行类型检查,而不是在运行时执行类型检查
2、C++ 完全支持面向对象的程序设计,包括面向对象开发的四大特性:封装 抽象 继承 多态
3、标准的 C++ 由三个重要部分组成:
- 核心语言,提供了所有构件块,包括变量、数据类型和常量,等等。
- C++ 标准库,提供了大量的函数,用于操作文件、字符串等。
- 标准模板库(STL),提供了大量的方法,用于操作数据结构等。
4、用 extern
声明外部变量是不能进行初始化的,只是声明而不是定义,声明是不会为变量开辟内存空间的,自然无法对其进行初始化的操作
5、左值可以在等号左右,但右值只能在等号右边。
- 左值在等号右边的应用例子:实际项目中,为了防止将
==
误写作=
推荐将变量名写在右侧,编译器可以帮助寻找错误
6、全局变量是指在所有函数外部声明的变量,包括主函数
- 全局变量从定义处开始至程序结束起作用,即全局变量存在有效作用域。如果它定义在了调用语句的后面,需要在该调用语句前加上extern对全局变量进行声明
局部变量是指在函数或一个代码块内部声明的变量,注意是代码块也可以,即一个{}
- 在程序中,局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值。
- 也可以通过域名
::
在函数中引用到全局变量,不加域名解析则引用局部变量
7、 字符 ‘0’:****char c = ‘0’; 它的 ASCII 码实际上是 48
**字符 ‘\0’**: ASCII 码为 0,表示一个字符串结束的标志。这是转义字符(整体视为一个字符)
8、不应把**true **的值简单的看成 1,把 false 的值简单的看成 0,而是真和假。
9、定义成 **const **后的常量,程序对其中只能读不能修改。因此必须在一开始定义的时候就完成初始化。下面这段语句两行都会报错
1 |
|
除非这个变量是用extern修饰的外部变量
1 |
|
10、预处理 #define 变量定义值以后,不能加分号,否则会连同分号一起直接替换掉定义的部分
11、两个整型数据相除结果依然是整型,会把小数部分直接舍去(不是四舍五入)
1 |
|
主函数
main 函数前面加上什么数据类型,比如: int void
main 函数的返回值是返回给主调进程,使主调进程得知被调用程序的运行结果。
标准规范中规定 main 函数的返回值为 int,一般约定返回 0 值时代表程序运行无错误,其它值均为错误号,但该约定并非强制。
如果程序的运行结果不需要返回给主调进程,或程序开发人员确认该状态并不重要,比如所有出错信息均在程序中有明确提示的情况下,可以不写 main 函数的返回值,写void main(){}
,这在一些检查不是很严格的编译器中,比如 VC, VS 等,void 类型的 main 是允许的
不过在一些检查严格的编译器下,比如 g++, 则要求 main 函数的返回值必须为 int 型,因此还是建议使用int作为返回值
区分main程序运行结果并以 int 型返回,是一个良好的编程习惯,尽量避免使用void main()
这种写法
13、int main() 和 int main(void) 的区别?
int main(void)
指的是此函数的参数为空,不能传入参数,如果你传入参数,就会出错。
int main()
表示可以传入参数。
在 C++ 中 int main() 和 int main(void) 是等效的
C++的变量自动转换
强制转换规则很简单,(待转换类型)(表达式)
。但自动转换需要遵循一定规则:
若参与运算量的类型不同,则先转换成同一类型,然后进行运算。
转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,先把int量转成long型后再进行运算。
若两种类型的字节数不同,转换成字节数高的类型
若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型
1
2
3int a = 1;
double b = 2.1;
cout << "a + b = " << a + b << endl; //输出为a + b = 3.1,先将a转为double再加上2.1
所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。
char型和short型参与运算时,必须先转换成int型。
在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度
1
2
3
4int a=1;
double b=2.5;
a=b;
cout << a; //输出为 2,丢失小数部分
typedef 声明
可以使用 typedef 为一个已有的类型取一个新的名字。
1 |
|
下面的语句会告诉编译器,feet 是 int 的另一个名称:
1 |
|
现在,下面的声明是完全合法的,它创建了一个整型变量 distance:
1 |
|
关于 typedef 的几点说明:
- typedef 可以声明各种类型名,但不能用来定义变量。
- 用typedef只是对已经存在的类型增加一个类型名,而没有创造新的类型
- 当在不同源文件中用到同一类型数据(尤其是像数组、指针、结构体、共用体等类型数据)时,常用 typedef 声明一些数据类型,把它们单独放在一个头文件中,然后在需要用到它们的文件中用 #include 命令把它们包含进来,以提高编程效率。
- 使用 typedef 有利于程序的通用与移植。有时程序会依赖于硬件特性,用 typedef 便于移植。
有关typedef 与 #define 的区别
执行时间不同
- 关键字 typedef 在编译阶段有效,由于是在编译阶段,因此 typedef 有类型检查的功能。
- #define 则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的字符串替换,而不进行任何检查。所以它没有typedef 来的稳健
功能有差异
typedef 用来定义类型的别名,定义与平台无关的数据类型,与 结构体的结合使用等。
比如定义一个叫 FALSE 的浮点类型,在目标平台一上,让它表示最高精度的类型为:
1
typedef long double FALSE;
在不支持 long double 的平台二上,改为
1
typedef double FALSE;
在连 double 都不支持的平台三上,改为:
1
typedef float FALSE;
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。标准库就广泛使用了这个技巧
#define 不只是可以为类型取别名,还可以定义常量、变量、编译开关等。
作用域不同
#define 没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。
typedef 有自己的作用域
1
2
3
4
5
6
7
8
9void func1()
{
typedef unsigned int UINT;
}
void func2()
{
UINT uValue = 5;//error C2065: 'UINT' : undeclared identifier
}
二者修饰指针类型时,作用不同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23typedef int * pint;
#define PINT int *
int i1 = 1, i2 = 2;
const pint p1 = &i1; //p不可更改,p指向的内容可以更改,相当于 int * const p;
const PINT p2 = &i2; //p可以更改,p指向的内容不能更改,相当于 const int *p;或 int const *p;
pint s1, s2; //s1和s2都是int型指针
PINT s3, s4; //相当于int * s3,s4;只有一个是指针。
void TestPointer()
{
cout << "p1:" << p1 << " *p1:" << *p1 << endl;
//p1 = &i2; //error C3892: 'p1' : you cannot assign to a variable that is const
*p1 = 5;
cout << "p1:" << p1 << " *p1:" << *p1 << endl;
cout << "p2:" << p2 << " *p2:" << *p2 << endl;
//*p2 = 10; //error C3892: 'p2' : you cannot assign to a variable that is const
p2 = &i1;
cout << "p2:" << p2 << " *p2:" << *p2 << endl;
}结果:
1
2
3
4p1:00EFD094 *p1:1
p1:00EFD094 *p1:5
p2:00EFD098 *p2:2
p2:00EFD094 *p2:5
宏定义 #define 和常量 const 的区别
类型和安全检查不同
宏定义是字符替换,没有数据类型的区别,同时这种替换没有类型安全检查,可能产生边际效应等错误;
const常量是常量的声明,有类型区别,需要在编译阶段进行类型检查
编译器处理不同
宏定义是一个”编译时”概念,在预处理阶段展开,不能对宏定义进行调试,生命周期结束与编译时期;
const常量是一个”运行时”概念,在程序运行使用,类似于一个只读行数据
存储方式不同
宏定义是直接替换,不会分配内存,存储与程序的代码段中;
const常量需要进行内存分配,存储与程序的数据段中
定义域不同
1 |
|
定义后能否取消
宏定义可以通过#undef
来使之前的宏定义失效
const常量定义后将在定义域内永久有效
1 |
|
是否可以做函数参数
宏定义不能作为参数传递给函数
const常量可以在函数的参数列表中出现
const关键字
const是constant的简写,只要一个变量前面用const来修饰,就意味着该变量里的数据可以被访问,不能被修改。也就是说const意味着只读(readonly)。
规则:const离谁近,谁就不能被修改;
const修饰一个变量,一定要给这个变量初始化值,若不初始化,后面就无法初始化。
本质:const在谁后面谁就不可以修改,const在最前面则将其后移一位,二者等效。
const关键字作用
- 为给读你代码的人传达非常有用的信息,声明一个参数为常量是为了告诉用户这个参数的应用目的;
- 通过给优化器一些附加信息,使关键字const也许能产生更紧凑的代码;
- 合理使用关键字const可以使编译器很自然的保护那些不希望被修改的参数,防止无意的代码修改,可以减少bug的出现;
const关键字应用
- 欲阻止一个变量被改变,可使用const,在定义该const变量时,需先初始化,以后就没有机会改变他了;
- 对指针而言,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
- 在一个函数声明中,const可以修饰形参表明他是一个输入参数,在函数内部不可以改变其值;
- 对于类的成员函数,有时候必须指定其为const类型,表明其是一个常函数,不能修改类的成员变量;
- 对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。