C++PrimerPlus知识点小结

xiaoxiao2025-02-13  19

C++融合了3种不同的编程方式:C语言代表的过程性语言,C++在C语言基础上添加的类代表的面向对象语言(oop),C++模板支持的泛型编程。C++11初始化方式 //大括号初始化,等号可以使用,也可以不使用 int emus{7}; int rheas={12}; //大括号内不包含任何东西,这种情况下变量将被初始化为零 int rocs={}; int psychics{}; C++存储整型的方式类似一个圈,负数存的是补码。可以手动模拟一下。第一位为1-9,则基数为10(十进制),第一位为0,则基数为8(8进制),前两位为0x或0X,则基数为16cout的一些特殊特性 /* cout提供了控制符dec,hex,oct分别用于指示cout以十进制, 十六进制和八进制格式显示整数。 */ int tmp=42; cout<<tmp<<endl; cout<<hex; cout<<tmp<<endl; cout<<oct; cout<<tmp<<endl; C++11强制类型转换运算符static_cast < typeName>(value) //使用要求更为严格,虽然不知道有什么用。。 char c='c'; cout<<static_cast<int>(c)<<endl; cin.get() cin会留下换行符 cin.getline 不会留下换行符C++11新增的另一种类型:原始(raw)字符串 cout<<R"(Jim "King" Tutt uses "\n" instead of endl.)"<<'\n'; //输出 Jim "King" Tutt uses \n instead of endl. C++结构体种的位字段 C++允许指定占用特定位数的结构成员,这使得创建与某个硬件设备上的寄存器对应的数据结构非常方便。字段的类型应为整型或者枚举,接下来是冒号,冒号后面跟着一个数字,它制定了位数。可以使用没有名称的字段提供间距。每个成员都被称为位字段。 struct torgle_register { unsigned int SN : 4; unsigned int : 4; bool goodIn : 1; bool goodTorgle : 1; }; torgle_register tr = {14,true,false}; if(tr.goodIn)puts("Yes"); 共用体 结构体可以同时存储int,long和double,共用体只能存储int,long或double union one4all { int int_val; long long_val; double double_val; }; one4all pail; pail.int_val=15; cout<<pail.int_val<<endl; pail.double_val=1.38; cout<<pail.double_val<<endl; cout<<pail.int_val<<endl; /* 输出 15 1.38 -515396076 */

由于共用体每次只能存储一个值,因此它必须有足够的空间来存储最大的成员,所以,共用体的长度为其最大的成员的长度。 匿名共用体:

struct widget { char brand[20]; int type; union { long id_num; char id_char[20]; }; }; widget prize; cin>>prize.type; if(prize.type==1)cin>>prize.id_num; else cin>>prize.id_char;

共用体常用于(但并非只能用于)节省内存。C++可用于嵌入式系统编程,如控制烤箱,MP3播放器或火星漫步者的处理器。对于这些应用程序来说,内存可能十分宝贵。另外,共用体常用于操作操作系统数据结构或硬件数据结构。

sizeof运算符和指针,数组之间的关系。 char *p="abc"; cout<<sizeof(p)<<endl;//指针长度,一律返回4 cout<<sizeof(*p)<<endl;//字符长度,1个字节 char a[]="abcdef"; cout<<sizeof(a)<<endl;//数组长度 7 cout<<sizeof(&a)<<endl;//指针长度,一律返回4 cout<<sizeof(*a)<<endl;//字符长度,1个字节 类型组合(P118) struct student { int year; }; //变量 student a1,a2,a3; a1.year=2003; a2.year=2005; //数组 student t[3]; t[0].year=2003; (t+1)->year=2004; //指针数组 const student *b[3]={&a1,&a2,&a3}; cout<<b[0]->year<<endl; //数组指针 const student **c=b; cout<<(*c)->year<<endl; cout<<c[0]->year<<endl; 基于范围的for循环(C++11)(P152) C++新增一种循环,基于范围(range-based)的for循环。 double prices[5]={4.99,1.99,6.87,7.99,8.49}; for(double x:prices) cout<<x<<endl; //要修改数组的元素,需要使用不同的循环变量语法 for(double &x:prices) x=x*0.8; //还可以使用基于范围的for循环和初始化列表 for(int x:{3,5,2,8,6}) cout<<x<<' '; cout<<endl; 文件尾条件(P155) 输入来自于文件时,使用一些特殊符号来表示结束有时会很难令人满意,这时我们可以通过检测文件尾(EOF)。 如果输入来自于键盘呢,很多操作系统都允许通过键盘来模拟文件尾条件。在Unix中,可以在行首按下Ctrl+D来实现。在Windows命令提示符模式下,可以在任意位置按Ctr+Z和Enter。 如果编程环境检测到EOF后,cin将两位(eofbit和failbit)都置为1.可以通过成员函数eof()来查看eofbit是否被设置;如果监测到EOF,则cin.eof()将返回true,否则返回false。同样,如果eofbit或failbit被置为1,则fail()成员函数返回true,否则返回false。 cin.fail()比cin.eof()更通用,因为它可以检测到其他失败原因,如磁盘故障。cin.get()有两种用法,一种ch=cin.get(),一种cin.get(ch)。写入及读取文件 //写入 ofstream outFile; ofstream fout; outFile.open("fish.txt"); char s[30]; scanf("%s",s); fout.open(s); outFile<<"hello world"<<endl; outFile.close(); fout.close(); //读取 ifstream inFile; //ifstream fin; inFile.open("fish.txt"); if(!inFile.is_open()) { puts("Error"); exit(EXIT_FAILURE); } char s[30]; while(inFile>>s) { cout<<s<<endl; } inFile.close(); //fin.close(); /* fish.txt hello world */ 使用数组区间的函数(P220) 迭代器原型 int sum_arr(const int *begin,const int *end) { int tot=0; for(const int *pt=begin;pt!=end;pt++) tot=tot+*pt; return tot; } int main() { sum[0]=1,sum[1]=2;sum[2]=3; cout<<sum_arr(sum,sum+3); } 指针与const(P222) 可以用两种不同的方式将const关键字用于指针,第一种方法是让指针指向一个常量对象,这样可以防止使用指针来修改所指向的值,第二种方法是将指针本身声明为常量,这样可以防止改变指针指向的位置。下面来看细节。 int age=30; const int *pt=&age; *pt += 1;//invalid const int age2=30; int *p2=&age2;//invalid

pt的什么并不意味着指向的值是一个常量,而只是意味着对pt而言,这个值是一个常量。我们可以通过age变量来修改age的值,但不能用pt指针来修改它。 第二种也不行。 如果指针指向指针,情况更复杂。 假设涉及的是一级间接关系,则将非const指针赋给const指针是可以的。

int age=39; int *pd=&age; const int *pt=pd;

但进入两级间接关系时,与一级间接关系一样将const和非const混合的指针赋值方式将不再安全,因此是不行的。如果允许这样做,则可以编写这样的代码:

const int **pp2; int *p1; const int n=13; pp2=&p1; *pp2=&n; *p1=10;

上述代码将非const地址(&p1)赋给了const指针pp2,因此可以使用p1来修改const数据。因此,这样是不行的。 如果数据类型本身并不是指针,则可以将const数据或非const数据的地址赋给指向const的指针,但只能将非const数据的地址赋给非const指针。

函数指针 声明时函数指针的返回类型以及参数列表和函数必须一样。 double pam(int); double (*pf)(int); int main() { pf=pam; double x=pam(4); double y=(*pf)(5); cout<<y<<endl; y=pf(5);//这样写也行 cout<<y<<endl; } double pam(int a) { return 1.0*a*a; }

深入探讨函数指针 假如现在有下面一些函数的原型,他们的参数列表和返回值类型相同:

const double *f1(const double ar[],int n); const double *f2(const double [],int n); const double *f3(const double *,int);

接下来,假设要声明一个指针,它可指向这三个函数之一。假定该指针名为p1:

const double *(*p1)(const double *,int); p1=f1; //还可以这样写 auto p2=f2;

现在来看下面的语句:

cout<<p1(a,3)<<' '<<*(p1(a,3))<<endl; cout<<p2(a,3)<<' '<<*(p2(a,3))<<endl; //前面返回的是地址,后面返回的是值

如果有一个函数指针数组,包含这三个函数,该怎么声明呢?

const double * (*pa[3])(const double *,int)={f1,f2,f3}

这里要说明一点,运算符[]的优先级高于*,其他的可以自己理解。 如果要声明一个函数指针数组指针,用来指向pa数组,该怎么声明呢?

//第一种 auto pc=&pa; //第二种 pd是一个指针,它指向一个包含三个元素的数组。 const double *(*(*pd)[3])(const double *,int)=&pa;

(有点没弄清楚)这样的声明方式应该是数组指针的声明。 要调用函数,需认识到这一点,既然pd指向数组,那么*pd就是数组,而(*pd)[i]就是数组中的元素,即函数指针。因此,较简单的函数调用是(*pd)[i](av,3),而 ∗ ( ∗ p d ) [ i ] ( a v , 3 ) \ast(\ast pd)[i](av,3) (pd)[i](av,3)是返回的指针指向的值。也可以使用第二种使用指针调用函数的语法,使用 ( ∗ ( ∗ p d ) [ i ] ) ( a v , 3 ) (\ast (\ast pd)[i])(av,3) ((pd)[i])(av,3)来调用函数,而 ∗ ( ∗ ( ∗ p d ) [ i ] ) ( a v , 3 ) \ast (\ast (\ast pd)[i])(av,3) ((pd)[i])(av,3)是指向的double的值。 请注意pa(它是数组名,表示地址)和&pa之间的差别。pa都是数组第一个元素的地址,即&pa[0]。因此,它是单个指针的地址。但&pa是整个数组(即三个指针块)的地址。从数字上说,pa和&pa的值相同,但它们的类型不同。一种差别是,pa+1为数组中下一个元素的地址,而&pa+1为数组pa后面一个12字节内存块的地址(假定一个地址为4个字节)。另一个差别是,要得到第一个元素的值,只需对pa解除一次引用,但需要对&pa解除两次引用: ∗ ∗ &amp; p a = = ∗ p a = = p a [ 0 ] ** \&amp;pa==*pa==pa[0] &pa==pa==pa[0] 使用typedef 进行简化 typedef 放在类型声明前,在变量的位置填上新类型的名字,这样新类型就可以当旧类型用,换句话说,声明变量时,在句首加上typedef,就相当于声明了新类型。

typedef const double *(*p_fun)(const double *,int); p_fun p1=f1; p_fun pa[3]={f1,f2,f3}; p_fun (*pd)[3]=&pa;

C++提供了许多新的函数特性,包括内联函数,按引用传递变量,默认的参数值,函数重载(多态)以及模板函数。

内联函数 内联函数的编译代码与其他的程序代码“内联”到一块,对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多内存。 应有选择的使用内联函数。如果执行函数代码的时间比处理函数调用机制的时间长,则节省的时间只占一小部分。如果代码执行时间很短,则内联调用就可以节省非内联调用使用的大部分时间。

临时变量,引用参数和const(P262) 如果引用参数是const,则编译器将在下面两种情况下生成临时变量:

实参的类型正确,但不是左值。实参的类型不正确,但可以转换成正确的类型。

左值:左值参数是可被引用的数据对象,例如,变量,数组元素,结构成员,引用和解除引用的指针都是左值。非左值包括字面常量(用引号括起的字符串除外,它们由其地址表示)和包含多项的表达式。在C语言中,左值最初指的是可出现在赋值语句左边的实体,但这是引入关键字const之前的情况。现在,常规变量和const变量都可视为左值,因为可通过地址访问他们。但常规变量属于可修改的左值,而const变量属于不可修改的左值。 简而言之,如果接受引用参数的函数的意图是修改作为参数传递的变量,则创建临时变量将阻止这种意图的实现。解决方法是,禁止这么做。现在C++标准正是这样。 如果只是使用传递的值,而不是修改它们,因此临时变量不会造成任何不利的影响,反而会使函数在可处理的参数种类方面更通用。

继承的一个特征是,派生类继承了基类的方法(如ofstream继承了ostream,ofstream可以使用ostream的一些方法),另一个特征是,基类引用可以指向派生类对象,而无需进行强制类型转换。

C++如何跟踪每一个重载函数呢?C++编译器将执行一些神奇的操作-名称修饰或名称矫正。它会根据函数原型中指定的形参类型对每个函数名进行加密。

函数模板

内存模型和名称空间

对象和类

转载请注明原文地址: https://www.6miu.com/read-5024658.html

最新回复(0)