C++笔记(三)——基础篇Ⅲ:零碎补充

  1. 带符号和无符号数的整型溢出行为
    在这里插入图片描述
  2. 以{}方式进行列表初始化:不允许缩窄
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
  1. C++11版本的校验表,编译器将依次查阅该列表。
    1)如果有一个操作数的类型是long double,则将另一个操作数转换为long double。
    2)否则,如果有一个操作数的类型是double,则将另一个操作数转换为double。
    3)否则,如果有一个操作数的类型是float,则将另一个操作数转换为float。
    4)否则,说明操作数都是整型,因此执行整型提升。
    5)在这种情况下,如果两个操作数都是有符号或无符号的,且其中一个操作数的级别比另一个低,则转换为级别高的类型。
    6)如果一个操作数为有符号的,另一个操作数为无符号的,且无符号操作数的级别比有符号操作数高,则将有符号操作数转换为无符号操作数所属的类型。
    7)否则,如果有符号类型可表示无符号类型的所有可能取值,则将无符号操作数转换为有符号操作数所属的类型。
    8)否则,将两个操作数都转换为有符号类型的无符号版本。

  2. 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
  1. 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);
  1. ·string类可以使用运算符**+将两个string对象合并起来,还可以使用运算符+=将字符串附加到string对象的末尾。
    字符串:可以使用函数
    strcpy()将字符串复制到字符数组中,使用函数strcat()**将字符串附加到字符数组末尾。
    ·将一行输入读取到string对象中:getline(cin,str)
    ·字符串赋值只复制地址,要获得字符串副本需要先根据长度分配内存,再用strcpy()或strncpy()进行复制。

  2. 共用体(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 
  1. 在不进行强制类型转换的情况下,只能将定义枚举时使用的枚举量赋给这种枚举的变量。
    枚举量是整型,可被提升为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
  1. 使用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
  1. 自动存储、静态存储和动态存储
    ①自动变量在所属的函数被调用时自动产生,在该函数结束时消亡。自动变量是一个局部变量,其作用域为包含它的代码块。自动变量通常存储在栈中,执行代码块时变量将依次加入到栈中,离开代码块时将按相反的顺序释放这些变量,即后进先出(LIFO)。
    ②静态存储是整个程序执行期间都存在的存储方式,使变量成为静态的方式有两种:一种是在函数外面定义它;另一种是在声明变量时使用关键字static
    ③动态存储:new和delete运算符提供了一种比自动变量和静态变量更灵活的方法,它们管理了一个内存池,被称为自由存储空间(free store)或(heap)。该内存池同用于静态变量和自动变量的内存是分开的。new和delete能够在一个函数中分配内存,而在另一个函数中释放它。

  2. 前缀递增、前缀递减和解除引用运算符的优先级相同,以从右到左的方式进行结合。

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
  1. 基于范围的for循环:对数组(或容器类,如vector和array)的每个元素执行相同的操作。
double prices[5]={4.9910.996.877.998.49}fordouble x : prices)
       cout <<x<< std::endl;
  1. cin.get(char)和cin.get()的区别
    (使用字符参数的版本更符合对象方式,因为其返回值是istream对象,这意味着可以将它们拼接起来。)
    在这里插入图片描述

  2. ·对于 || 运算符,先执行左边的表达式,再执行右边的表达式;如果左侧的表达式为true,则不会去判定右侧的表达式。
    ·对于&&运算符,同样先判定左侧,并且在右侧被判定之前产生所有的副作用。如果左侧为false,则整个逻辑表达式必定为false,将不会再对右侧进行判定。
    ·逻辑OR和逻辑AND运算符的优先级都低于关系运算符;!运算符的优先级高于所有的关系运算符和算术运算符;逻辑AND运算符的优先级高于逻辑OR运算符。
    (!>算术运算符>关系运算符>&&>||)

  3. C++将数组名视为指针,并将数组名解释为其第一个元素的地址。
    该规则有一些例外:首先,数组声明使用数组名来标记存储位置;其次,对数组名使用sizeof将得到整个数组的长度(以字节为单位);第三,将地址运算符&用于数组名时,将返回整个数组的地址

  4. 当(且仅当)用于函数头或函数原型中,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组成的数组。因此,指针类型指定了列数,这就是没有将列数作为独立的函数参数进行传递的原因。

  5. 要将字符串作为参数传递给函数,则表示字符串的方式有三种:①char数组;②用引号括起的字符串常量(也称字符串字面值);③被设置为字符串的地址的char指针。
    ·将字符串作为参数来传递,但实际传递的是字符串第一个字符的地址,即字符串函数原型应将其表示字符串的形参声明为char*类型

  6. 获取函数地址时,要将函数作为参数进行传递,使用函数名(后面不跟参数)即可。

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
  1. 内联函数在编译时将使用相应的函数代码替换函数调用。对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多内存
    ·在函数声明前加上关键字inline;函数定义前加上关键字inline。通常的做法是省略原型,将整个定义(即函数头和所有函数代码)放在本应提供原型的地方。
    ·内联函数不能递归

  2. 引用变量必须在声明时进行初始化;可以通过初始化声明来设置引用,但不能通过赋值来设置
    ·基类引用可以指向派生类对象,而无需进行强制类型转换。这种特征的一个实际结果是,可以定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数,也可以将派生类对象作为参数
    ·对于使用传递的值而不作修改的函数:
    ①如果数据对象很小,如内置数据类型或小型结构,则按值传递
    ②如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向const的指针
    ③如果数据对象是较大的结构,则使用const 指针或const引用,以提高程序的效率,节省复制结构所需的时间和空间;
    ④如果数据对象是类对象,则使用const引用
    ·对于修改调用函数中数据的函数:
    ①如果数据对象是内置数据类型,则使用指针
    ②如果数据对象是数组,则只能使用指针
    ③如果数据对象是结构,则使用引用或指针
    ④如果数据对象是类对象,则使用引用

  3. 使用被重载的函数时,需要在函数调用中使用正确的参数类型。没有匹配的原型并不会自动停止使用其中的某个函数,C++将尝试使用标准类型转换强制进行匹配。如果有多种转换方式,C++将拒绝这种函数调用,并将其视为错误。
    ·编译器在检查函数特征标时,将把类型引用和类型本身视为同一个特征标。不允许返回类型不同,而特征标相同的函数进行重载。
    ·匹配函数时,并不区分const和非const变量。将非const值赋给const变量是合法的,但反之则是非法的。

  4. ·函数模板允许以任意类型的方式来定义函数。如建立一个交换模板:

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 T1class T2>{
…
void ft(Tl x,T2 y)
decltype(x+y) xpy=x+y;
…
}

①如果expression是一个没有用括号括起的标识符,则var的类型与该标识符的类型相同,包括const等限定符;
②如果expression是一个函数调用,则var的类型与函数的返回类型相同;
③如果expression是一个左值,则var为指向其类型的引用,expression是用括号括起的标识符;
④如果前面的条件都不满足,则var的类型与expression的类型相同。

  1. 要创建链接性为外部的静态持续变量,必须在代码块的外面声明它;要创建链接性为内部的静态持续变量,必须在代码块的外面声明它,并使用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全局变量的链接性为内部。

  1. new找不到请求的内存量时,将引发异常std::bad_alloc。
    ·定位new运算符能够指定要使用的位置。要使用定位new特性,首先需要包含头文件new,它提供了这种版本的new运算符的原型;然后将new运算符用于提供了所需地址的参数。除需要指定参数外,句法与常规new运算符相同。
pd1=new double[N];//use heap 
pd2=new(buffer)double[N];// use buffer array
  1. 名称空间:
    ·在默认情况下,在名称空间中声明的名称的链接性为外部的(除非它引用了常量)。
    ·任何名称空间中的名称都不会与其他名称空间中的名称发生冲突
    ·通过作用域解析运算符:: 访问给定名称空间中的名称。
    ·using声明使特定的标识符可用,using编译指令使整个名称空间可用。
    ·一般说来,使用using声明比使用using编译指令更安全
    ①using声明只导入指定的名称,如果该名称与局部名称发生冲突,编译器将发出指示
    ②using编译指令导入所有名称,包括可能并不需要的名称,如果与局部名称发生冲突,则局部名称将覆盖名称空间版本,而编译器并不会发出警告
发布了12 篇原创文章 · 获赞 0 · 访问量 133

猜你喜欢

转载自blog.csdn.net/weixin_43279709/article/details/104717931