C++ Primer Plus(第六版)读书笔记(一)

C++初学者~
函数分为2个部分:函数头,函数体。函数名后面的括号中的部分叫做形参列表,它描述的是从调用函数传递给被调用的函数的信息。
sizeof运算符,对类型名(如int)使用时,应将名称放在括号中;但对变量名(如n_short)使用时,括号可选。
头文件limits(见书P41-P42)
无符号整型与有符号整型的区别(书P44)
默认情况下,cout以十进制的格式显示数据,也可以通过cout<
cout<<”Year =”<<1492<<”\n”,除非使用了特殊的后缀或值太大,一般默认int,L表示long,U表示unsigned等。
字符赋值用’’,字符串赋值用””。
char ch;
 cin>>ch;//输入53,只显示一个5,此时输入的数字是字符,一次只能读入一个字符,故只读入了字符5。
注意C++转义序列编码(见书P50)
关于整型short、int、long、long long,short至少16位(有符-32768—32767,无符0--65535),int至少与short一样长,long至少32位,且至少与int一样长,long long至少64位,且至少与long一样长。
cout.setf(ios_base::fixed,ios_base::floatfield),迫使输出使用定点表示方法,以便更好地了解精度,防止程序把较大的值切换为E表示法,并使程序显示到小数点后6位。
(Pr 3.8 P57)
float型只能显示数字的前6到7位(Pr3.9 P58)
以{}的方式进行初始化的变量类型的转化,如char c2 = {66},则c2就为ASCII的”B”。
只有在定义数组时才能使用初始化。
论’\0’对字符串char定义初始化{}的重要性,也可以用双引号来定义,此时就不需要’\0’。在确定存储字符串所需的最短数组时,别忘了将结尾的空字符计算在内。(P74-P75)
cout中空白分隔的字符串常量都自动拼成一个,cin中使用空白来确定字符串的结束位置。(Pr4.3 P77)
cin中的类成员函数(面向行的输入)cin.getline()和cin.get(),即都是用于读取一行字符串,后者将一直读取输入,直到到达行尾或读取了size-1个字符为止,将换行符作为字符串输入的结束,并将其留在输入队列中,所以为了为下一行输入做准备,要用cin.get(name,size).get() (Pr9.9 P315)。前者通过换行符来确定行尾,但不保存换行符,在存储字符串时,它用空字符来代替换换行符,可以使用cin.ignore()来消除下一行的输出影响。(P338T1) (Pr4.5 P80)
string类可以当做一种数据类型来进行赋值、拼接和附加。string str;getline(str,size)
char与string的差别:对于char类型的字符,赋值用函数strcopy,拼接用函数strcat,而string类型的字符串可以直接用’=’进行赋值,用’+’进行拼接;计算字符串的长度时,char类型的用strlen(char)函数,而string类型的用str.size()函数.
函数strlen计算未初始化的数组的长度时,从数组的第一个元素开始计算字节数,直到遇到空字符,有可能在数组末尾的几个字节后才遇到空字符,故会出现计算未初始化的数组长度时,计算值比数组本身的长度要长。
未被初始化的string类型的参数str被自动设置为0。
C++11原始字符串表示方法,R”(和)”,如果原始字符串中本身包含(),则用R”+*(标识原始字符串的开头,用)+*”标识原始字符串的结尾。
C++中结构的用法,与C中结构的用法相似,用关键字struct定义了一种数据结构后,就可以创建这种类型的变量,而且与C中不同的,声明结构变量时,关键字struct可省略。例如:
struct inflatable
{
     ……
};
inflatable hat,则hat是一种inflatable类型的数据 。
外部声明可以用在文件的所有函数中,局部声明只能用在这个函数中。
共用体中的成员共用一个地址,顾每次只有一个成员在存储。
枚举,一种定义变量的结构方式,包括枚举变量和它们对应的整型值,枚举值的设置和赋值范围(见P97)
 int higgens=5;
   int * pt=&higgens;
   这个语句试讲pt的值设置为 &higgens
在C++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存。故要在对指针应用解除引用运算符*之前,将指针初始化为一个确定的、适当的地址。
指针与数字:
int * pt;
pt=(int *)0xB8000000;
pt中存的地址是0xB8000000。
指针指向的数据类型的长度不一样,但是指针本身存的地址的长度是一样的,指针本身的长度都为4个字节。(Pr4.17 P103)
只能用delete来释放使用new分配的内存,总的来说,一定要配对使用new和delete。
通过声明来创建的数组,程序在编译时给数组分配的内存被称为静态联编,即必须在编写程序时指定数组的长度;但使用new时,运行阶段需要数组就创建,不需要就不创建,这种被称为动态联编,程序将在运行阶段时确定数组的长度。
在C++中,对数组应用sizeof运算符得到的是数组的长度。
short tell[20]
 short * pw = new short[20]/ short (*pw) [20] = &tell
两者的差别就在于,short * pw = new short[20]中pw指向数组的第一个元素,内存块的长度为2;short (*pw) [20] = &tell中pw指向一个内存块为40的数组,是一个指针数组,pw+1后,指针移动40个字节。(Pr4.19 P107)
(int *)强制类型转换用于用一个指向字符串的指针来显示地址(Pr4.20 P111)
如何实现按输入的字符串的长度来分配内存以达到节省内存分配,如下例所示:
 char temp [20];
 cin>>temp;
 char *pn = new char[strlen(temp)+1];//计算输入的temp的长度
 strcpy(pn,temp);(Pr4.22 P116)


指针数组,
year_end 
{
    int year;
};
year_end s0,s1,s2;
year_end * arp [3]={&s0,&s1,&s2};
arp是一个数组名称,它是一个元素的地址,第一个元素是一个指向s0结构体的指针。故arp是一个指针数组。
int (* arp)[3]//arp就是数组指针。(Pr4.23 P119)
※知识点补充:

int arr[10];
int *p1=arr;
int*p2=&arr[0];//p1==p2
int (*p)[10]=&arr;//arr==&arr[0]==&arr,但是p是指针。(P213)


int a=1,b=2,c=3;
int *arp[3]={&a,&b,&c};
int **ppa=arp;//那么*ppa==&a
*ppa==arp,arp等于第一个指针的地址。

int a1[3]={1,2,3};
cout<
a1[-1]=4;//这里是指找到a1指向的地方,向前移动1个int元素,并把4存储到该位置(Pr4.24 P121)
在C++的for循环中,测试表达式的值并没有限制为只能真或者假,可以使用任意表达式,C++将把结果强制转换为bool类型,值为0的表达式将转换为bool值false,导致循环结束。(Pr5.2 P127)
x
当判定表达式的值这种操作改变了内存中数据的值,就说表达式有副作用。
string word;
word[i];//string类的功能之一(Pr5.6 P133)
顺序点是程序执行过程中的一个点,在这里,进入下一步之前将确保对所有的副作用都进行了评估。
前缀格式和后缀格式最终的效果是一样的。(P135)
char *str=”hello”;
char ch = * str++;//*(str++)后缀++用的是原本的值
char ch = * ++str;//*(++str)前缀用的是副作用后的值
复合语句中,如果在语句块中定义了一个新的变量,则仅当程序执行该语句块中的语句时,该变量才存在。(P138)
逗号运算符,它确保先计算第一个表达式,再计算第二个表达式,值得注意的是,逗号运算符的优先级最低。(P138-P140)
如果word是数组名,那么word==”mate”;//数组名是数组的地址,同样,引号括起来的字符串常量也是地址,因此,这句语句是在判断它们是否存储在相同的地址上。
基于范围的for循环(P152)
循环和文本输入,cin将忽略空格和换行符,cin.getchar()空字符也算在内,故以上2个函数对输入字符串的识别与计数要采用哨兵字符。cin.get()的文件尾条件,通过其fail()成员函数来判断。cin.get(ch)与cin.get()是有区别的(P159-表5.3)(P152-P159)
cin.get(ch1).get(ch2),这条语句是将输入的下一个字符读入到ch1中,并将接下来的一个字符读入到ch2中。
char ch
cout<<++ch;//这里输出字符
cout<
使用cin输入字符串的相关问题。cin 使用空白(空格、制表符和换行符)来定字符串的界,这意味着cin在获取字符数组输入时只读取一个单词,在读取该单词后,cin将该字符串放到数组中,并自动在结尾添加空字符。这样,后一个字符串将不会输入到数组中。当输入字符串可能比目标数组长时,将不能防止。例如将包含30个字符的字符串放到20个字符的数组中的情况。
ctype字符库函数(P179)
用枚举量作标签,cin无法识别枚举量,switch语句和while语句会将int值和枚举量进行比较,将枚举量提升为int。(P184-6.11)
continue跳过循环体剩余的部分,开始新一轮循环;break跳过循环的剩余部分,到达下一条语句。
double array[5];
  while(cin>>array[i])//如果输入成功,则转换为bool值true,否则为false。如果发现输入的对象不是正确格式,可以通过cin.clear来重置cin以接受新的输入,且要与cin.get()!=’\n’配套使用,只用不用,错误的输入不能删掉,只用,cin的输入流被错误的输入占据,不能再输入。
  cin.clear();
  while(cin.get()!=’\n’)
       continue;          (P189-Pr6.14)
使用文件输出的主要步骤如下:1.包含头文件fstream;2.创建一个ofstream对象;3.将该ofstream对象同一个文件关联起来;4.就像使用cout那样使用该ofstream对象。在程序运行之前,被写入的文件,如果不存在,则open()函数将新建一个此名的文件;如果存在,则open()函数将首先截断该文件,即将其长度截短到零,丢弃原来的内容,然后将新的输出加入到该文件中。
cout<
cout.precision(2);//遵循四舍五入,显示2位十进制数,比如:输入12.3456,显示12。输入12345,显示1.2e+004。
cout.self(ios_base::showpoint);//显示6位十进制数(包括小数位),不足补0。
cout<
cout.precision(2);//这2句连在一起用,遵循四舍五入,保留到小数点后2位。(P192-Pr6.15)
读取文本文件时,检查文件是否被打开至关重要。(P195-Pr6.16)
传递数组时,函数将使用原来的数组,而不是副本。
int cookies[8];//cookies==&cookies[0]==&cookies
  int arr[ArSize];//arr[i]== *(arr+i),&arr[i]==arr+i,这里的arr是当做指针用的(P214-Pr7.6)
填充数组时,for循环,每次循环结束一次,i将比最后一个数组索引大1.(P218-Pr7.7)
int age = 39;
  const int *pt=&age;//可以通过age来修改age的值,但是不能使用pt指针来修改它
  int sage=80;
  pt=&sage;//可以将一个新地址赋给pt
  int const *ps=&sage;
  *ps=20;//可以用ps来修改值,且ps只能指向sage
  const int *const p =&age;//不能通过p来修改age的值,且p只能指向age(P223-P224)

int data[3][4]={{1,2,3,4},{5,6,7,8},{2,4,6,8}};
  int sum(int (* arr)[4],int size);//int (*arr)[4]是一个指向数组指针,数组的类型是由4个int型的数组成的,这里函数中的二维数组的形参还可以表示为int sum(int arr[][4],int size)。*arr[i]=arr[i][0],cout<
  int total=sum(data,3);//arr[r][c]==*(*(arr+r)+c)


字符串指针指向字符串数组
  char arr[20]="Hello world !";
  char *ps=arr;//ps指向arr数组的第一个元素
  cout<

如果需要多个字符串,可以声明一个string对象数组,而不是二维char数组。


关于array对象,对于array *arr,(*arr)[i]是元素值。注意指针的初始化。



 const double * f1(const double ar[],int n);
  const double * f2(const double [],int n);
  const double * f3(const double *,int n);
  auto p2=f2;//(*p2)来调用f2
  const double * (*pa[3])(const double *,int n)={f1,f2,f3};
  auto pb=pa;//pb指向pa数组第一个元素的指针,即*pb==pa
  const double *(*(*pd)[3])(const double ar[],int n)=&pa;
  pd指向数组,那么*pd就是数组,而(*pd)[i]是数组中的元素。即函数指针。因此,(*(*pd)[3])(const double ar[],int n)是调用函数。由于返回值的类型为const double *,实际返回的是一个double值的地址。

必须在引用时将其初始化。引用就像const的作用一样,一旦关联起来,这2个变量就是值和地址相同的。按值传递与按引用传递,后者能起到和指针一样的效果,前者的函数用的是函数本身的变量,变量值来自副本,而不是原始值本身的变量。


为了防止引用变量修改相关联的原始变量的值,可以使用const,这就相当于const型指针,可以通过原始变量的值改变其值,但不能通过指针(引用变量)来改变原始值。
 
函数在引用参数是const时,创建临时变量的2种情况
  实参的类型正确,但不是左值(左值参数是可被引用的数据对象,例如,变量、数组元素、结构成员、引用和解除引用的指针都是左值);
  实参的类型不正确,但可以转换为正确的类型。(P262-263)
常规函数的返回类型为右值。返回引用时应主要引用的变量不能是函数中定义的变量,因为一旦跳出这个函数,这个变量就不存在了,它只是函数中的一个临时变量。结构的引用和指针指向结构的原理很类似。(P266-P267)
默认参数指的是当函数调用中省略了实参时自动使用的一个值。对于带参数列表的函数,必须从右向左添加默认值。(P274-P255)
函数重载是指它们完成相同的工作,但使用不同的参数列表(也称为函数特征标)。编译器把类型引用和类型本身视为同一个特征标。非const值可以赋给const值,但反之是非法的。重载函数返回类型可以不同但特征标也必须不同。
void sink(double & r1);//匹配可修改的左值
  void sank(const double & r2);//匹配可修改左值,const左值,右值
  void sunk(double &&r3);//引用地址。匹配左值
对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及它们的重载版本。如果有多个原型,则编译器在选择原型时,非模板版本优先于显示具体化和模板版本,而显示具体化优先于使用模板生成的版本。
显示实例化和显示具体化在同一文件中不可同时使用。(P288)
关于#ifndef的说明,头文件管理(P302)
C++11所用的四种方案来存储数据(P304)
代码块是由花括号括起的一系列语句。
自动存储持续性的作用域为局部,没有链接性。自动变量用栈的方式来存储,新的数据被象征性地放在原有数据的上面,当程序使用完后,将其从栈中删除。(P308)
静态持续变量有3种链接性:外部链接性(可在其他文件中访问)、内部链接性(只在当前文件中访问)和无链接性(只能在当前函数或代码块中访问)。(P309)
零初始化和常量表达式初始化统称为静态初始化。(P310-中)
链接性为外部的变量通常简称为外部变量,它们的存在持续性为静态,作用域为整个文件。外部变量是在函数外部定义的,因此对所有函数而言都是外部的。例如,可以在mian()前面或头文件中定义它们。(P310-下)
单定义规则,变量只能有一次定义,C++提供了2种变量声明,一种是定义声明或简称为定义,它给变量分配存储空间;另一种是引用声明或简称为声明,它不给变量分配存储空间,因为它引用已有的变量。引用声明使用关键字extern,且不进行初始化;否则,声明为定义,导致分配存储空间。(P311)
在全局变量前,加上关键字static,该变量就被定义成为一个静态全局变量,static限定符用于作用域为整个文件的变量,该变量的链接性将为内部的,全局静态变量是显式用static修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用。在局部变量前,加上关键字static,该变量就被定义成为一个静态局部变量,静态局部变量有以下特点:该变量在全局数据区分配内存;静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束。
静态存储,无链接性的相当于每次代码块被调用时被初始化。
volatile的作用是:作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
C++如何查找函数,如果该文件中的函数原型指出该函数是静态的,则编译器将只在该文件中查找函数定义,在默认情况下,函数的链接性为外部,即可以在文件间共享,故包含函数的文件要被放到调用其的函数所在的程序中作为头文件被编译。(P318-319)
通常,编译器使用三块独立的内存:一块用于静态变量,一块用于自动变量,另一块用于动态存储。
动态内存---new,初始化、函数和替换函数、作为定位运算符(P321-P322,Pr9.10)
  char buffer [20];
  int *p1,*p2;
  p1=new(buffer)int[10];//在buffer所在的存储区域从起始位置开始分10个字节给p
  p2=new(buffer+2*sizeof(int))int[10];//相对起始位置偏移2*4个字节
指针的类型的强制转换用(void *)
用namespace定义一个名称空间,用using进行声明或编译来调用其中的变量(P326-P311)
类成员函数,使用作用域解析运算符(::)来标识函数所属的类;类方法(即类声明里的公共成员函数)可以访问类的private组件。
内联定义,实现多个文件程序中的所有文件都可用这个函数,要求是在每个使用它们的文件中都对其进行定义。定义位于类声明中的函数都将自动成为内联函数,类声明之外定义成员,用inline限定符。
创建了类对象后,可以声明类变量,也可以使用new为类对象分配存储空间,可以将对象作为函数的返回值和参数,也可以将一个对象赋给另一个。
客户/服务器模型,客户就是使用类的程序,类声明(包括类方法)构成了服务器,它是程序可以使用的资源。(P350)
类构造函数,专门用于构造新对象、将值赋给它们的数据成员。构造函数没有返回值和声明类型。值得注意的是,尽量避免区分构造函数的参数名与类本身的成员名。(P353)
构造函数有2种,一种是声明和定义的构造函数,可以有默认参数;另一种是默认构造函数。默认构造函数是在未提供显示初始值来创建对象的构造函数,例如: Stock food;该对象使用显示默认构造函数。值得注意的是,如果已经有为类定义的构造函数,则同时需要提供默认构造函数!(P354)
析构函数的定义,在类名称前加~。析构函数没有返回值和声明类型。
下面两条语句有根本性的差别:
  Stock stock2=Stock(“boff  object”,2,2.0);
  stock1=Stock(“Nifty Foods”,10,50.0);
第一条语句是初始化,它创建有指定值的对象,可能会创建临时对象(也可能不会);第二条语句是赋值,像这样的赋值语句中使用构造函数总会导致在赋值前创建一个临时对象。
只要类方法不修改调用对象,就应该将其声明为const(P362)
当类方法涉及到2个类对象时,调用另一个对象的对象可以用this指针的解引用*this来作为其别名。(P365)
用户创建一个类的多个对象,可使用对象数组。可以用构造函数来初始化数组元素。可以对不同的元素使用不同的构造函数,省略了构造函数的对象用隐式默认构造函数。
类成员函数和类数据成员之间的区别在于,如果创建给定类的多个对象,则每个对象都有其自己的数据存储空间,但所有的对象都使用同一组成员函数。
类声明只是描述了对象的形式,只有当创建对象时,将有用于存储的内存空间。
不要返回指向局部变量或临时对象的引用。函数执行完毕后,局部变量和临时对象将消失,引用将指向不存在的数据。
友元函数是在类声明中声明的,但它不是成员函数,不能使用成员运算符来调用;不要在定义中使用关键字friend,也可以直接选择在声明中使用内联定义。
重载运算符,可以选择使用成员函数或者非成员函数,一般来说,非成员函数应为友元函数。

你可能感兴趣的:(C++)