C++PrimerPlus知识点小结

版权声明:转载之前请说你是博主的粉丝哦。 https://blog.csdn.net/qq_34921856/article/details/83500754
  1. C++融合了3种不同的编程方式:C语言代表的过程性语言,C++在C语言基础上添加的类代表的面向对象语言(oop),C++模板支持的泛型编程。
  2. C++11初始化方式
     //大括号初始化,等号可以使用,也可以不使用
    int emus{7};
    int rheas={12};
    //大括号内不包含任何东西,这种情况下变量将被初始化为零
    int rocs={};
    int psychics{};
  1. C++存储整型的方式类似一个圈,负数存的是补码。可以手动模拟一下。
  2. 第一位为1-9,则基数为10(十进制),第一位为0,则基数为8(8进制),前两位为0x或0X,则基数为16
  3. cout的一些特殊特性
    /*
    cout提供了控制符dec,hex,oct分别用于指示cout以十进制,
    十六进制和八进制格式显示整数。
    */
    int tmp=42;
    cout<<tmp<<endl;
    cout<<hex;
    cout<<tmp<<endl;
    cout<<oct;
    cout<<tmp<<endl;
  1. C++11强制类型转换运算符static_cast < typeName>(value)
    //使用要求更为严格,虽然不知道有什么用。。
    char c='c';
    cout<<static_cast<int>(c)<<endl;
  1. cin.get() cin会留下换行符 cin.getline 不会留下换行符
  2. C++11新增的另一种类型:原始(raw)字符串
    cout<<R"(Jim "King" Tutt uses "\n" instead of endl.)"<<'\n';
    //输出 Jim "King" Tutt uses \n instead of endl.
  1. 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");
  1. 共用体
    结构体可以同时存储int,long和double,共用体只能存储int,longdouble
    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播放器或火星漫步者的处理器。对于这些应用程序来说,内存可能十分宝贵。另外,共用体常用于操作操作系统数据结构或硬件数据结构。

  1. 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个字节
  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;
  1. 基于范围的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;
  1. 文件尾条件(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()更通用,因为它可以检测到其他失败原因,如磁盘故障。
  2. cin.get()有两种用法,一种ch=cin.get(),一种cin.get(ch)。
  3. 写入及读取文件
	//写入
	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
    */
  1. 使用数组区间的函数(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);
}
  1. 指针与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指针。

  1. 函数指针
    声明时函数指针的返回类型以及参数列表和函数必须一样。
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) 是返回的指针指向的值。也可以使用第二种使用指针调用函数的语法,使用 ( ( p d ) [ i ] ) ( a v , 3 ) (\ast (\ast pd)[i])(av,3) 来调用函数,而 ( ( p d ) [ i ] ) ( a v , 3 ) \ast (\ast (\ast 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]
使用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;
  1. C++提供了许多新的函数特性,包括内联函数,按引用传递变量,默认的参数值,函数重载(多态)以及模板函数。

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

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

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

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

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

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

  6. 函数模板

  7. 内存模型和名称空间

  8. 对象和类

猜你喜欢

转载自blog.csdn.net/qq_34921856/article/details/83500754