C++ Primer 详解(第二章)
2018-06-17 23:32:41来源:未知 阅读 ()
2.1 基本内置类型
1)基本类型
1,注意区分无符号数和有符号数,对于char 类型,分为3种,unsigned char ,signed char ,char ,char 和其他两种之所以不一 样,是因为在一些机器上是char 是带符号的,一些机器上是不带符号的,这对于移植性有很大问题。尽量不要使用char做算术运算,如果实在需要,请指明是unsigne char,还是signed char.
2,关于类型的选择:
①如果明确知道不会为负,使用无符号类型。
②对于int型,小的使用int,实在比较大使用long long即可,short 太小,long和int一样大。
③对于浮点型,一般使用double,因为float位数太小,而且对于运算速度和double相比没什么差别,long double精度太高,没有必要,而且由于long double精度太高,带来的运算代价太高。
2)关于类型的转换
1.类型转换一般以等号左边的为基准
bool —— int 真为1,假为0。
int —— bool 0为假,负数和正数都为真。
浮点型 —— int 只取整数,不四舍五入
3.对于无符号类型,赋值超出范围后,所得到的值是取模后的数。
4.对于有符号类型,赋值超出范围后,所得到的数是未定义的。
5.在一个表达式中,如果又有有符号数,又有无符号数,最终有符号数会被转换为无符号数。(最好别混用)
6.一个机器上short类型占16位,那么可赋值的最大数为2^15-1.
3)字面值常量
1.浮点数的表示用e或者E表示指数
2. 每个字面值都有其默认的类型,比如 5的默认类型是10进制,5.42的默认类型是double。在进行赋值的时候,会进行类型 转换。例如: float val=5.42;其中5.42默认是double类型的,在赋值前会进行类型转换,这种转换有时候会报错,因此可以修改字面值得默认类型。
1. 一个典型的例子:使用C++11 新规则初始化一个数
float fVal{12.5};//由于12.5默认是double,现在要转换为float精度会损失,编译器就会报错。
但是:
float fVal{12.5F};//12.5F默认类型是float,没有精度损失,编译器不会报错。
2.后缀修改字面值默认类型:(一般用于数字)
u || U unsiged 用于整型
l || L long 用于整型
ll || LL long long 用于整型
b || B 二进制
f || F float 用于浮点型
l || L long double 用于浮点型
3.类型修改:
前缀修改字面值默认类型:(一般用于字符)
u Unicode 16字符
U Unicode 32字符
L 宽字符
u8 UTF-8
0 8进制
0x 16进制
2.2变量
1)变量的定义
1.变量的定义: 一个基本数据类型+一组声明符(为什么是一组,因为声明符可以相互修饰)
2.建议初始化每个内置类型变量,指针。(优秀代码)
3.初始化和赋值完全不同,初始化时在创建这个变量的时候,赋予一个初始值,赋值是擦除原来的值,用一个新的值代替原来的。
4.区别初始化和赋值就是再看有赋值符号的时候,赋值符号左边有没有一个新的变量产生。
5.初始化的方式一般4种:
int units_sold=0;
int units_sold={0};
int units_sold(0);
int units_sold{0};
其中花括号叫列表初始化,C++ 11新特性,可以防止精度损失。
2)变量的声明&定义
1.为了能将程序拆分开编辑,C++语言是支持分离式编译的。为了支持分离式编译。C++把变量的声明和定义区分开来。声明:使得名字为程序可知,一个文件如果想使用在别处定义变量,就必须有对这个变量的声明。声明不会分配内存。 定义:负责创建与名字相关联的实体。会分配内存。声明可以声明多次,但是定义只能定义一次。
2.声明一个变量在前面添加extern.例如extern int i;
3.在声明一个变量的时候,不要初始化,这样会抵消extern的作用。
4.P41页(中文版)一句话:声明和定义的区别看起来也许微不足道,但是实际上却非常重要,如果要在多个文件中使用同一个变量,就必须将声明和定义分离。此时,变量的定义必须出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却不能重复声明。困惑:以前刚开始学C++的时候,喜欢用全局变量,在我的眼里,定义在非函数体内的变量就是全局变量。比如定义在Test.h 定义一个int i=0;然后其他文件包含这个头文件不一样也可以用么。为什么非要extern。嗯,后来去实践了一下,在编译链接的时候,会报错,重复定义同一个变量。而只在头文件extern 一个变量,然后在任何cpp文件中定义一次这个变量,就不会再报错了,同时不用include 其他文件,编译器会帮你找到你所声明的变量的定义的地方,这就是extern得作用。函数声明是默认extern的,这个得注意。
由此想到。static的作用。static的作用是使得这个变量只在这个文件中有效,和extern是相反的,因此在一个头文件定义一个静态变量,其他文件再包含这个文件,编译链接的时候也不会出错。
3)标识符
标识符没什么好说的,命名规范:开头必须是字母或者下划线,整个标识符只可以由数字,字母,下划线组成。
命名有两种风格:1.C风格全小写字母:int stau_code 2.面向对象风格:stauCode.选择一种,坚持下去
4)作用域
一般来说,作用域是用花括号分割开来的{};值得注意的是花括号{}是可以单独存在的,表示一个单独的作用域。
2.3复合类型
1)引用
1.记得Effective C++的中文版,翻译者侯捷老师在前言里面提到,一些感觉中文翻译不够理想的,他便保留原文不译,其中reference便是其中一个。在一些其他书里面,reference被翻译为别名。争议存在就肯定有其道理,因此不要按照中文的引用的原意去理解这个类型~。
2.定义一个引用类型:一个类型说明符+&声明符,并被初始化。
3.由于引用类型是默认const类型的,而const类型在定义的时候必须初始化,且不可被“赋值”(严格的说这样的说法是错误的因为引用不是对象,但是可以这样理解)。
4.引用类型是只是并非一个Object,它只是一个名字,它的定义不会被分配内存,自身没有地址,它仅仅是其他对象的“绰号”。
5.由于第3点的原因,不能定义一个引用的引用类型。(引用的实现和指针相关,它没有地址,所以不能被引用)。
6.一定得注意的:除了一些特殊的情况,所有的引用类型必须都必须和与之绑定的对象类型严格匹配。不然同一个对象会拥有两种类型。
2)指针
1.指针(pointer,point to)是用来实现对一个对象的另外一种访问方式。
2.不要混合指针和引用。指针是一个对象,它自己也有地址,不是默认const类型。
3.定义一个指针类型:一个类型说明符+*声明符,并被初始化。
4.注意:声明符必须放在每个变量前面。
5.P47页(中文版)有句话:因为引用不是对象,没有实际地址,所以不能定义指向引用的指针。读到这句话的时候,想了下,指向引用的指针不是应该会被指向被引用的对象么。为什么不可以,在VS上进行实验,也能运行。难道书上错了或者是编译器优化?后来,细细解读指向引用的指针:
[cpp] view plain copy
char &*pStr=pStrCout
这才是指向引用的指针类型.
6.前面提到过,定义个指针或者内置类型的变量的时候,最好赋一个初始值。给指针赋没有确定的情况下最好赋为空。 0,NULL,nullptr.
3)理解复合类型的声明
1.最重要的:变量的声明为一个类型说明符+一组声名符,声明符为一组并且可以相互修饰,例如上面的char &*,int *&.这种,记住一个原则:往往越靠近变量的声明符对变量的影响越大(从右到左)比如char &*,首先它是一个char类型的指针这个指针指向的是一个引用。
int *&首先它是一个int类型的引用,这个引用引用的对象是一个指针。
2.4 const限定符
1.记住const与非const是两种不同的类型,很多成员函数可以因为是否有const特性而重载,而很多人却忽略了这个事实。
1)const引用
1.const限定符只是告诉编译器这个变量的值是不能被write,只要是不修改它的值,其他的任何操作都是正确的。
2.const对象,由于被定义后值不能再被改变,因此在定义的时候必须初始化。
3.如果想在多个文件中共享const对象,必须在变量定义之前添加extern。
4.const对象分为编译时常量和定义时常量,这两种常量有一定的区别,比如数组定义的时候,要定义一个定义时常量。
5.给一个普通引用,指针赋值的时候,等号右边不能是一个常量对象.记住,。(其实不止是不能是一个常量,由于指针的特殊性,给指针赋值的时候,一定要考虑好这个值是不是真的能赋值给这个指针)
6.const的特殊性:前面提到,一个引用引用的类型必须是一个对象。但是对于一个const引用是一个例外。比如想要获得一个对象的内容为42的对象的引用。我们正常的代码是int uVal=42;int &rVal=uVal;但是由于const的特殊性,编译器会帮我们做这个优化,我们可以直接这样定义:const int &rVal=42;道理同样,编译起帮我做了int uVal=42这一步,问题来了,为什么const可以这样做,其他非const对象不能这样写。再看一个例子:double dVal=42.423;想要定义一个引用引用这个dVal,但是由于某些原因,这个引用需要是int类型的,我们会这样做:int uTemp=dVal; int &rVal=uTempl;在int uTemp=dVal发生了隐式转换,然后再赋值给这个int类型的引用,同上,对于const 对象,我们可以直接const int &rVal=dVal;编译器会帮我做上面的步骤,因为我们保证了dVal是不会改变的,如果改变rVal,但是rVal引用的实际是uTemp,dVal并没有改变。所以必须是const。
7.深入理解引用:可以通过引用看到赋值和初始化的区别:给一个非常量引用初始化的时候,类型必须严格一致,但是给在赋值的时候却可以发生类型转换。例如:
int i=0, &r1=i;
double d=0,&r2=d;
可以这样:r1=r2;r1=4.32;都不会发生错误。但是int &i=d;就会报错,这就是初始化和赋值的区别。
2)指针与const
1.指向常量的指针:一个指针指向一个常量对象:const double pi=3.14;那么这个指针的定义:const double *ptr=π(还是按照前面所说的:离变量越近的声明符对变量影响越大:首先这是一个double类型的指针,这个指针指向的时一个常量)(指向常量的指针可以指向非常量对象)
2.常量指针:常量指针是说的一个指针,这个指针是一个常量。类似引用那样:double *const ptr=π(理解:ptr是一个常量,它的值不可以改变,这个常量是一double型的指针)。
3.如上说的,常量指针是一有类似引用的属性,它定义的时候必须初始化。
4.指向常量的指针和常量指针区别:
指向常量的指针所指向的对象是一个常量。*ptr不能改变,但是指针所指向的对象ptr能改变
常量指针是指针本身是一个常量,指针只能一直指向这个对象,不能改变,但是所指向的对象的值可以改变。
3)顶层const
1.顶层const:对象本身是常量,底层const:所指对象是const(一般用于指指针或者引用)
2.顶层const比较随意,并且赋值和被赋值都没有要求,但是底层const必须注意:当等号左边是一个非底层const的时候,等号右边不能是一个非const对象,而当左边是一个底层const对象的时候,右边可以随意.
举例:const int c=1; const int &r=c; //正确 int &rc=c;//错误
4)constexpr表达式(目前好像还是有些编译器不支持此关键字)
1.constexpr定义:[C++11] ①值不会改变 ②编译过程常量
2.constexpr和const的区别:const 是指这个值是不会被改变的。而且分为编译时常量和运行时常量而constexpr是指在编译期期间就可以确定的常量,简单的说就是比const还const的常量,
3.constexpr可以用来修饰函数。例如{return 42;}这种函数,但是编译器并不负责检查这个constexpr是否修饰成功,人工检测方法:将这个函数拿来作为数组的大小。比如int array[fn()];若没有报错则表示修饰成功。
4.constexpr在用来修饰指针的时候,这个指针要么是必须初始化为空,要么指向的对象的地址是固定不变的(static对象,全局对象);
5.并没有所谓的指向constexpr对象的指针,constexpr只能修饰指针。constexpr int *q=nullptr; q是常量指针。
2.5 处理类型
1)类型别名
1.类型别名有两种定义方法:
①:typedef double wages; // wages=double typedef wage *p //p=double *
②using SI=double;//SI=double
2.值得注意的理解:
typedef char *pstring;
const pstring cstr=0;//注意其中cstr是一个常量指针
const pstring *ps; ps是一个指针,指向一个指向char类型对象的指针。
错误的理解:将pstring换成char *.比如 const char * cstr=0;按照这样的理解,cstr应该是一个指向常量的char类型的指针。。然而在typedef这个定义中,把char*作为一个整体取了一个类型别名pstring,在const pstring cstr=0;中,const是修饰的pstring这个整体,也就是说修饰的是指针而不是char。
2)auto类型说明符
1.auto(自动类型说明符)是通过等号右边进而推断等号左边的类型。int ci=42; auto index=ci;//index被推断为int;
2.auto会忽略顶层const特性而保留底层const特性(想想吧,它不得不这样做,要是不保留底层const特性,那么赋值便是错误的,那为什么要忽略顶层const特性呢,这是为了保留可扩展性,然用户自己选择是否真的需要const,万一只是需要一个变量呢,而真正需要const可以看第3条)
3.若想给auto加上顶层const特性:const int ci=42; const auto f=ci;
3)decltype 类型指示符
1.decltype(类型提取说明符)是通过表达式的返回类型而推断出类型。decltype(fon()) sum=x;(与auto不同之处,当想用这个表达式的返回值初始化这个变量的时候,用auto,只需要它的类型,而不需要这个值得时候,用decltype)。
2.decltype是类型提取,必须提取完全,因此他会保留所有的顶层和底层const特性。
3要给一个decltype修饰的变量加上const特性,const decltype(fon()) f=42;
4.值得注意的地方:int i=42,*p=&i,&r=i;
decltype(r+0) b;//因为r是一个引用类型,decltype得到的是引用类型,r+0返回的时int类型,则b是一个int类型。
decltype(*p) c;//c是一个引用。为什么:因为p为一个指针,而对这个指针解引用得到这个指针所指的变量,但是这个变量有个特性,就是可以作为表达式的左值,因此这是一个引用类型,更加详细的说明见第5条。
5.decltype((i)) e;//e是一个引用,因为对i加一个括号修饰符,decltype会把它提取为一个表达式,一个变量可以作为赋值语句的左值,这种类型和引用具有完全相同的特性,因此e被作为一个引用,
2.6自定义类型
1)编写自己的头文件
1.不管需要不需要,最好给头文件加上防止多重定义的预处理符:
①:#ifndef XXXX
#define XXXX
#endif
②:#pragram once
2.头文件一般放的东西:
1.函数的声明
2.需要在多个文件中使用的变量的声明
3.类或者结构体
4.模版
5.inline函数
6.const变量
7.类函数成员,类数据成员的声明
8.预处理命令
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- C++ 转换函数搭配友元函数 2020-06-10
- C++ 自动转换和强制类型转换(用户自定义类类型) 2020-06-10
- C++ rand函数 2020-06-10
- C++ 友元函数 2020-06-10
- C++ 运算符重载 2020-06-10
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash