- 带符号和无符号数的整型溢出行为
- 以{}方式进行列表初始化:不允许缩窄。
const int code=66;
int x=66;
char c1 {31325};// narrowing,not allowed
char c2={66};// allowed because char can hold 66
char c3 {code};//ditto
char c4={x);// not allowed,x is not constant
x=31325;
charc5=x;//allowed by this form of initialization
-
C++11版本的校验表,编译器将依次查阅该列表。
1)如果有一个操作数的类型是long double,则将另一个操作数转换为long double。
2)否则,如果有一个操作数的类型是double,则将另一个操作数转换为double。
3)否则,如果有一个操作数的类型是float,则将另一个操作数转换为float。
4)否则,说明操作数都是整型,因此执行整型提升。
5)在这种情况下,如果两个操作数都是有符号或无符号的,且其中一个操作数的级别比另一个低,则转换为级别高的类型。
6)如果一个操作数为有符号的,另一个操作数为无符号的,且无符号操作数的级别比有符号操作数高,则将有符号操作数转换为无符号操作数所属的类型。
7)否则,如果有符号类型可表示无符号类型的所有可能取值,则将无符号操作数转换为有符号操作数所属的类型。
8)否则,将两个操作数都转换为有符号类型的无符号版本。 -
cin.get()跨过换行符的方法:
①使用不带任何参数的cin.get()调用可读取下一个字符(即使是换行符),因此可以用它来处理换行符,为读取下一行输入做好准备。
cin.get(name,Arsize);//read first line
cin.get();//read newline
cin.get(dessert,Arsize);//read second line
②将两个类成员函数拼接起来(合并)
cin.get(name,Arsize).get();//concatenate member functions
- 当cin读取数字,将回车键生成的换行符留在了输入队列中,后面的cin.getline()看到换行符后,将认为是一个空行。两种解决方法:
①使用没有参数的get()和使用接受一个char参数的get()
cin>>year;
cin. get();//or cin. get(ch);
②利用表达式cin>>year返回cin对象
(cin>>year).get();//or(cin>> year).get(ch);
-
·string类可以使用运算符**+将两个string对象合并起来,还可以使用运算符+=将字符串附加到string对象的末尾。
字符串:可以使用函数strcpy()将字符串复制到字符数组中,使用函数strcat()**将字符串附加到字符数组末尾。
·将一行输入读取到string对象中:getline(cin,str)
·字符串赋值只复制地址,要获得字符串副本需要先根据长度分配内存,再用strcpy()或strncpy()进行复制。 -
共用体(union)是一种数据格式,能够存储不同的数据类型,但只能同时存储其中的一种类型。
由于共用体每次只能存储一个值,因此它必须有足够的空间来存储最大的成员,所以,共用体的长度为其最大成员的长度。
union one4all{
int int_val;
long long_val;
double double_val;
};
one4all pail;
pail.int_val=15;//store an int
pail.double_val=1.38;//store a double,int value is lost
- 在不进行强制类型转换的情况下,只能将定义枚举时使用的枚举量赋给这种枚举的变量。
枚举量是整型,可被提升为int类型,但int类型不能自动转换为枚举类型。
int值可以通过强制类型转换,将它赋给枚举变量。
band=blue;//valid,blue is an enumerator
band=2000;//invalid,2000 not an enumerator
int color=blue;//valid,spectrum type promoted to int
band=3;//invalid,int not converted to spectrum
color=3+red;// valid,red converted to int
band=spectrum(3);//typecast 3 to type spectrum
对于枚举,只定义了赋值运算符,没有定义算术运算。
band=orange;//valid
++band;//not valid
band =orange+red;//not valid,but a little tricky
- 使用new分配内存:new将找到一个长度正确的内存块,并返回该内存块的地址。
只能用delete来释放使用new分配的内存。不能释放已经释放的内存块,但对空指针使用delete是安全的。
int* pn=new int;
使用new创建动态数组:将数组的元素类型和元素数目告诉new。
对于使用new创建的数组,应使用delete[]来释放。
int *psome=new int [10];// get a block of 10 ints
delete[] psome;//free a dynamic array
使用动态数组:不能修改数组名的值;但指针是变量,因此可以修改它的值。
double *p3 = new double [3];
p3[0] = 0.2;
p3[1] = 0.5;
p3[2] = 0.8;
p3 = p3 + 1;//p3[1] = 0.5
-
自动存储、静态存储和动态存储
①自动变量在所属的函数被调用时自动产生,在该函数结束时消亡。自动变量是一个局部变量,其作用域为包含它的代码块。自动变量通常存储在栈中,执行代码块时变量将依次加入到栈中,离开代码块时将按相反的顺序释放这些变量,即后进先出(LIFO)。
②静态存储是整个程序执行期间都存在的存储方式,使变量成为静态的方式有两种:一种是在函数外面定义它;另一种是在声明变量时使用关键字static。
③动态存储:new和delete运算符提供了一种比自动变量和静态变量更灵活的方法,它们管理了一个内存池,被称为自由存储空间(free store)或堆(heap)。该内存池同用于静态变量和自动变量的内存是分开的。new和delete能够在一个函数中分配内存,而在另一个函数中释放它。 -
前缀递增、前缀递减和解除引用运算符的优先级相同,以从右到左的方式进行结合。
double x=*++pt;//increment pointer,take the value;i.e.,arr[2],or 23.4
++*pt;//increment the pointed to value;i.e.,change 23.4 to 24.4
后缀递增和后缀递减的优先级相同,但比前缀运算符的优先级高,这两个运算符以从左到右的方式进行结合。
(*pt)++;//increment pointed-to value
x=*pt++;//dereference original location,then increment pointer
- 基于范围的for循环:对数组(或容器类,如vector和array)的每个元素执行相同的操作。
double prices[5]={4.99,10.99,6.87,7.99,8.49};
for(double x : prices)
cout <<x<< std::endl;
-
cin.get(char)和cin.get()的区别
(使用字符参数的版本更符合对象方式,因为其返回值是istream对象,这意味着可以将它们拼接起来。)
-
·对于 || 运算符,先执行左边的表达式,再执行右边的表达式;如果左侧的表达式为true,则不会去判定右侧的表达式。
·对于&&运算符,同样先判定左侧,并且在右侧被判定之前产生所有的副作用。如果左侧为false,则整个逻辑表达式必定为false,将不会再对右侧进行判定。
·逻辑OR和逻辑AND运算符的优先级都低于关系运算符;!运算符的优先级高于所有的关系运算符和算术运算符;逻辑AND运算符的优先级高于逻辑OR运算符。
(!>算术运算符>关系运算符>&&>||) -
C++将数组名视为指针,并将数组名解释为其第一个元素的地址。
该规则有一些例外:首先,数组声明使用数组名来标记存储位置;其次,对数组名使用sizeof将得到整个数组的长度(以字节为单位);第三,将地址运算符&用于数组名时,将返回整个数组的地址 -
当(且仅当)用于函数头或函数原型中,int* arr和int arr[]的含义才是相同的。
·函数参数含二维数组的声明格式:
①int sum(int(*ar2)[4],int size);//size为行数
括号是必不可少的,因为下面的声明将声明一个由4个指向int的指针组成的数组,而不是由一个指向由4个int组成的数组的指针;另外,函数参数不能是数组。
②int sum(int ar2[][4],int size);
上述两个原型都指出,ar2是指针而不是数组。指针类型指出,它指向由4个int组成的数组。因此,指针类型指定了列数,这就是没有将列数作为独立的函数参数进行传递的原因。 -
要将字符串作为参数传递给函数,则表示字符串的方式有三种:①char数组;②用引号括起的字符串常量(也称字符串字面值);③被设置为字符串的地址的char指针。
·将字符串作为参数来传递,但实际传递的是字符串第一个字符的地址,即字符串函数原型应将其表示字符串的形参声明为char*类型。 -
获取函数地址时,要将函数作为参数进行传递,使用函数名(后面不跟参数)即可。
process(think);//passes address of think () to process ()
thought(think());//passes return value of think () to thought ()
·声明函数指针:要声明指向特定类型的函数的指针,可以首先编写这种函数的原型,然后用(*pf)替换函数名,pf就是这类函数的指针。
使用(*pf)时,只需将它看作函数名即可。
double pam(int);// prototype
double (*pf)(int);//pf points to a function that takes one int argument and that returns type double
double *pf(int);//pf()a function that returns a pointer-to-double
·使用typedef对函数指针进行简化:
typedef const double*(*p_fun)(const double*,int);//pfun now a type name
p_fun p1=f1;//pl points to the f1()
p_fun pa[3]={f1,f2,f3};//pa an array of 3 function pointers
p_fun(*pd)[3]=&pa;//pd points to an array of 3 function pointers
-
内联函数在编译时将使用相应的函数代码替换函数调用。对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多内存。
·在函数声明前加上关键字inline;函数定义前加上关键字inline。通常的做法是省略原型,将整个定义(即函数头和所有函数代码)放在本应提供原型的地方。
·内联函数不能递归。 -
引用变量必须在声明时进行初始化;可以通过初始化声明来设置引用,但不能通过赋值来设置。
·基类引用可以指向派生类对象,而无需进行强制类型转换。这种特征的一个实际结果是,可以定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数,也可以将派生类对象作为参数。
·对于使用传递的值而不作修改的函数:
①如果数据对象很小,如内置数据类型或小型结构,则按值传递;
②如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向const的指针;
③如果数据对象是较大的结构,则使用const 指针或const引用,以提高程序的效率,节省复制结构所需的时间和空间;
④如果数据对象是类对象,则使用const引用。
·对于修改调用函数中数据的函数:
①如果数据对象是内置数据类型,则使用指针;
②如果数据对象是数组,则只能使用指针;
③如果数据对象是结构,则使用引用或指针;
④如果数据对象是类对象,则使用引用。 -
使用被重载的函数时,需要在函数调用中使用正确的参数类型。没有匹配的原型并不会自动停止使用其中的某个函数,C++将尝试使用标准类型转换强制进行匹配。如果有多种转换方式,C++将拒绝这种函数调用,并将其视为错误。
·编译器在检查函数特征标时,将把类型引用和类型本身视为同一个特征标。不允许返回类型不同,而特征标相同的函数进行重载。
·匹配函数时,并不区分const和非const变量。将非const值赋给const变量是合法的,但反之则是非法的。 -
·函数模板允许以任意类型的方式来定义函数。如建立一个交换模板:
template <typename AnyType>
void Swap(AnyType &a,AnyType &b){
AnyType temp;
temp=a;
a=b;
b=temp;
}
·关键字template和typename是必需的,除非可以使用关键字class代替typename。另外,必须使用尖括号。类型名可以任意选择。
·对于给定的函数名,可以有非模板函数、模板函数和显式具体化模板函数以及它们的重载版本。显式具体化的原型和定义应以打头,并通过名称来指出类型。具体化优先于常规模板,而非模板函数优先于具体化和常规模板。
·用于交换job结构的非模板函数、模板函数和具体化的原型:
//non template function prototype
void Swap(job&,job&);
//template prototype
template <typename T>
void Swap(T&,T&);
//explicit specialization for the job type
template<>void Swap<job>(job&,job&);//或template<>void Swap(job&,job&);
·为使函数调用参数与可行的候选函数的参数匹配所需要进行的转换。通常,从最佳到最差的顺序如下所述:
①完全匹配,但常规函数优先于模板。
②提升转换(例如,char和shorts自动转换为int,float自动转换为double)。
③标准转换(例如,int转换为char,long转换为double)。
④用户定义的转换,如类声明中定义的转换。
·关键字decltype提供了确定类型的解决方案:声明decltype(expression)var;
template<class T1,class T2>{
…
void ft(Tl x,T2 y)
decltype(x+y) xpy=x+y;
…
}
①如果expression是一个没有用括号括起的标识符,则var的类型与该标识符的类型相同,包括const等限定符;
②如果expression是一个函数调用,则var的类型与函数的返回类型相同;
③如果expression是一个左值,则var为指向其类型的引用,expression是用括号括起的标识符;
④如果前面的条件都不满足,则var的类型与expression的类型相同。
- 要创建链接性为外部的静态持续变量,必须在代码块的外面声明它;要创建链接性为内部的静态持续变量,必须在代码块的外面声明它,并使用static限定符;要创建没有链接性的静态持续变量,必须在代码块内声明它,并使用static限定符。
int global=1000;//static duration,external linkage
static int one_file=50;//static duration,internal linkage
int main(){…}
void funct1(int n){
static int count=0;//static duration,no linkage
int llama=0;
}
24. 引用声明使用关键字extern,且不进行初始化;否则,声明为定义,导致分配存储空间。
double up;//definition,up is 0
extern int blem;//blem defined elsewhere
extern char gr='z';//definition because initialized
·如果要在多个文件中使用外部变量,只需在一个文件中包含该变量的定义(单定义规则),但在使用该变量的其他所有文件中,都必须使用关键字extern声明它。
·默认情况下全局变量的链接性为外部的,但const全局变量的链接性为内部。
- new找不到请求的内存量时,将引发异常std::bad_alloc。
·定位new运算符能够指定要使用的位置。要使用定位new特性,首先需要包含头文件new,它提供了这种版本的new运算符的原型;然后将new运算符用于提供了所需地址的参数。除需要指定参数外,句法与常规new运算符相同。
pd1=new double[N];//use heap
pd2=new(buffer)double[N];// use buffer array
- 名称空间:
·在默认情况下,在名称空间中声明的名称的链接性为外部的(除非它引用了常量)。
·任何名称空间中的名称都不会与其他名称空间中的名称发生冲突。
·通过作用域解析运算符:: 访问给定名称空间中的名称。
·using声明使特定的标识符可用,using编译指令使整个名称空间可用。
·一般说来,使用using声明比使用using编译指令更安全:
①using声明只导入指定的名称,如果该名称与局部名称发生冲突,编译器将发出指示;
②using编译指令导入所有名称,包括可能并不需要的名称,如果与局部名称发生冲突,则局部名称将覆盖名称空间版本,而编译器并不会发出警告。