C++笔记

第1章 预备知识

  1. C++融合了3种不同的编程方式:C语言代表的过程性语言、C++在C语言基础上添加的类代表的面向对象语言、C++模板支持的泛型编程。
  2. 面向过程强调算法,面向对象强调数据。类是一种规范,它描述了这种新型数据格式,对象是根据这种规范构造的特定数据结构。即类是抽象的,对象是具体的。类是模板,对象是实例。

第2章 开始学习C++

  1. C++能够使用printf()、scanf()和其他所有标准C输入和输出函数,只需要包含常规C语言的stdio.h文件。
  2. namespace的使用方法:
    using namespace std;  所有的名称都可以使用
    using std::cout;     只使用std里面的cout。
  3. 类是用户定义的一种数据类型。
  4. C++程序应当为程序中使用的每个函数提供原型。C++不允许将函数定义嵌套在另一个函数定义中。
  5. main()函数的返回值返回给操作系统。即返回1则程序正确执行。

第3章 处理数据

  1. 面向对象编程(OOP)的本质是设计并扩展自己的数据类型。
  2. 基本类型:整数和浮点数; 复合类型:数组、字符串、指针和结构。
  3. 最小长度:short:16位;int:至少与short一样长;long:至少32位,且至少与int一样长;long long:至少64位,且至少与long一样长。(1个字节8位)
  4. 整数相加减时,如果超越了限制,其值将为范围另一端的取值。例:unsigned short类型的0-1将得到65535。
  5. cin只接受输入流中的第一个字符,其余字符会继续存放在流中,后续可以使用。
  6. 浮点数在计算机中分成两部分存储,一部分表示值,另一部分用于对值进行放大和缩小。
  7. 浮点数的缺点: 浮点数运算的速度比整数的慢,并且精度会降低。
    例:
1
2
3
float a = 2.34E+22f;
float b = a + 1.0f;
cout << b - a << endl;

  输出的结果应为1,但实际的输出值为0。因为a是一个小数点左边有23位的数字,加上1,即在第23位加1,而float类型只能表示数字中的前6位或前7位,即有效位为6或7,因此修改第23位对这个值并没有任何影响。
  对于float,C++只能保证6位有效位。

  1. C++会自动执行很多类型转换,但当较大整型转换为较小的整型时,原来的值 可能超出目标类型的取值范围,通常只复制右边的字节,即高位将被截掉。
    例:
1
2
int a = 7.2E12;
cout << a << endl;

  由于a的值超过了int的最大取值范围,因此在某些操作系统上,得到的值为2147483647(int类型的最大值)。

  1. C++类型转换:typeName(value)

第4章 复合类型

  1. 只有在定义数组时才能使用初始化,初始化时可以省略等号,不能将一个数组赋给另一个数组。如果只对数组的一部分初始化,则其他位置为0,如果初始化数组时,方括号内为空,则编译器会计算元素个数。
    例:
1
short a[] = {1, 2, 3, 4};

  编译器会计算short型数组包含4个元素。
  short指定了类型,a表示变量名,[]表明是一个数组。

  1. 字符串与字符数组
1
2
char NJUT[4] = {'n', 'j', 'u', 't'};
char NJUT_2[5] = {'n', 'j', 'u', 't', '\0'};

  这2个数组都是char数组,但只有第2个是字符串。即字符串必须是以’\0’结尾的字符数组。对于第2个字符数组,cout会打印’njut’,即遇到’\0’则停止,但对于第1个,会一直打印,直到遇到内存中有’\0’为止。
  还有1种更简单的方法是用一个引号将这些字符括起来,即

1
char NJUT[] = "njut";

  这种称为字符串常量,编译器会自动将’\0’加上。”njut”指的是字符串所在的内存地址。

1
2
3
char name[10] = "cxx";
cout << strlen(name);
cout << sizeof(name);

  strlen()返回的是存储在数组中的字符串的个数,即可见的字符个数,因此该返回值为3,而sizeof()计算的是整个数组的长度。如果要存储一个字符串,则数组的长度至少是strlen(字符串)+1。

  1. cin、cin.getline()和cin.get()
    cin:使用空白(空格,制表符和换行符)来确定字符串的结束位置。所以cin只能1次读取1个单词(遇到空格会结束),剩下的部分会继续存放在cin流中,直到下次的读取。
    cin.getline(name,size):函数读取整行,通过换行符确定字符串的结束位置,第1个参数为用来存储输入的数组名称,第2个为要读取的字符串的字符数,其值为size-1,即最后1位用于字符串结尾的空字符。
    cin.get():其中一种形式get(name,size)和getline(name,size)类似,都读到行尾,但get()不会读取并丢弃换行符,而是会留在输入流中,这样就会存在一个问题,例:
    1
    2
    cin.get(name1, 10);
    cin.get(name2, 10);

  当读完第1个名字后,由于get()并不会丢弃换行符,所以第2次读取的时候会直接读到换行符,从而会认为已经到达行尾,不会读取任何字符。为了避免这个问题,可以采用无参数的get()函数,即cin.get(),该函数读取下一个字符(换行符也可以),即采用下面的方法:

1
2
3
cin.get(name1, 10);
cin.get()
cin.get(name2, 10);

  也可以将这2个函数拼在一起,即cin.get(name1, 10).get()。

  1. string对象和字符数组之间的主要区别是:可以将string对象声明为简单变量,而不是数组。
    1
    2
    string name;
    string name = "cxx";

  string相当于一个类,字符串相当于类的实例化,不需要在string后面加上长度。

  1. 创建结构体时首先要创建一个模板。例
1
2
3
4
5
6
7
8
9
10
11
struct inflatable
{
char name[20];
float volume;
};

inflatable guest =
{
"Glorious Gloria";
1.88;
};

  前一个为结构体的声明,后一个为结构体的赋值。结构体相当于用户自定义的一种结构类型。struct指定了类型为结构体,inflatable为这种新类型的名称。大括号的内容为结构体的成员。整体相当于一个对象的模板,后面的则是对象的实例化,guest为结构体的名称,其具体内容为大括号里面的内容。
  注意,不管是声明还是赋值,都是C++的语句,需要加分号结束。

  1. 可以创建多个值相同的枚举量
1
enum number {first, second, third = 1. forth = 100, fifth};

  first在默认情况下为0,second和third都为1,fifth为101。枚举也相当于一个数据类型,初始化相当于模板,使用时需要先实例化,在调用里面的枚举值,即:

1
2
number mynumber;
mynumber = first;

  类似于const限定符。

  1. 只有初始化指针时,才会出现*=&,其他情况,*指的是解除引用,即取出地址存放的值,&指的是地址运算符,即获得变量存在的地址。
    1
    2
    int a = 5;
    int * pt = &a;

  相当于

1
2
int * pt;
pt = &a;

  int指定了类型为整型,pt是变量名,*表明是指针,和数组,结构体类似,指针也是个复合类型,需要同基本类型同时使用,即pt是一个整型的指针类型。
  指针指的是地址,只有在初始化时才可以将*和&同时使用,否则指针变量后面的赋值一定是某个地址。

  1. 在对指针变量使用解除引用运算符(*)之前,一定要将指针初始化为一个确定的,适当的地址。否则指针将找不到该地址,将错误的地址,甚至是正在运行的程序地址返回并执行操作。

  2. 一定要配对的使用new和delete,否则会发生内存泄漏。delete只能删除new创建的指针。

  3. 指针和字符串

    1
    2
    char flower[10] = "rose";
    cout << flower << "s are red.\n;

  如果给cout提供一个字符的地址,则将从这个字符开始打印,直到遇到空字符为止。

  1. 指针与数组、字符串、结构体
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    double wages[3] = {100.0, 200.0, 300.0};
    double * p1 = wages;

    char animal[10] = "bear";
    char * p2 = animal;

    struct info
    {
    int year;
    };
    info s01;
    info * p3 = &s01;

  指针在初始化时,后面需要赋值一个地址,数组名和字符串常量都是地址,所以不需要再加上取地址符。

  1. vector和array
    1
    2
    3
    4
    5
    6
    vector<typename> vt(n_elem);
    array<typename, n_elem> arr;

    double a1[4] = {1.2, 2.3, 3.4, 4.5};
    vector<double> a2(4);
    array<double, 4> a3 = {1.2, 2.3, 3.4, 4.5};

  使用vector和array,需要添加头文件#include和#include,且都可以使用标准的数组索引访问数组中的元素。

  1. new创建指针
    1
    typeName * pointer_name = new typeName;

第5章 循环和关系表达式

  1. for循环:
    1
    2
    3
    4
    for (初始化;测试语句;更新语句)
    {
    主体;
    }

  测试语句可以是任意表达式,C++会将结果强制转换为bool类型。
  C++中,在for和括号之间加上一个空格,以区别函数名和括号。

  1. 1
    cout.setf(ios_base::boolalpha);

  设置显示为布尔值。

第6章 分支语句和逻辑运算符

  1. c++有if - else if - else 结构

  2. isalpha():测试字符是否为字母字符,isdigits():测试字符是否为数字字符,isspace():测试字符是否为空白(换行符,空格和制表符),ispunct():测试字符是否为标点符号。

第7章 函数

  1. 如果声明的返回值类型为double,而函数返回一个int表达式,则该int值会将强制转换为double类型。

  2. C++的返回值不可以是数组,但可以将数组作为结构或对象组成部分来返回。

  3. 在函数中使用指针来处理数组

    1
    2
    int sum_arr(int * arr, int n); //函数原型
    int sum = sum_arr(cookies, 10); //函数调用

  这里的cookies为数组名,表示数组中第一个元素的地址,而函数原型中,* arr表示的也是地址,当然也可以用arr[]替换,因为在c++中,当且仅当用于函数头或函数原型中,* arr 和 arr[]的含义是相同的。
  在使用数组名作为参数时,并没有将数组的全部内容传递给函数,而是将数组的地址,包含的元素类型以及数目传递给函数,这样会大大减少内存空间。

  1. 指针常量和常量指针
    1
    2
    const int * pt = &age;  //常量指针
    int * const finger = &sloth; //指针常量

  变量名左边是变量的类型,当有多个类型修饰时,看最近的。
  变量pt最左边是*,代表的是指针,再往左是int,表明基本类型为整型,最左边是const,表明其值是一个常量。所以pt首先是一个指针,其存放的内容是变量age的地址,地址指向的是一个整型数据,且该数据是个常量,即const int 是共同修饰*pt的。所以变量pt指向的值是不可以改变的,即地址不可变。
  而变量finger最左边是const,代表的是常量,再往左是 *,表明是一个指针,最左边是int,表明基本类型为整型。所以finger首先是一个常量,只是该常量指向的是一个int型的指针,所以其指向的变量不可以更改。

1
2
3
4
5
6
7
8
9
10
int gorp = 16;
int chips = 12;

const int * p_snack = &gorp;
*p_snack = 20; //禁止修改
p_snack = &chips; //可以修改

int * const p_snack = &gorp;
*p_snack = 20; //可以修改
p_snack = &chips; //禁止修改

  第一个创建的是常量指针,所以指针指向的地址不可以修改,该例中,p_snack指针指向的是gorp的地址,而该地址存放的变量值是16,所以*p_snack的值只能是16,不能是其他值。但是指针指向的变量是可以更改的,即原来是指向gorp变量地址的,现在可以改为指向chips变量地址,但其值仍为16。虽然不能直接修改,但可以通过改变chips的值修改,比如将chips的值改为20,那么现在该常量指针的值就是20了。
  第二个创建的是指针常量,所以指针指向的变量是不可以修改的。也就是变量p_snack是一个常量,其值是不可以修改的,只不过这个变量是指针,而指针的值是地址,所以p_snack只能指向chips变量,其值等于chips的地址,但可以修改该地址对应的值,即本来存放chips变量的地址,其值是12,可以修改为20。

  1. 二维数组与指针

    1
    arr[a][b] = *(*(arr +a) + b)
  2. 函数与字符串数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #include <iostream>
    #include <string>
    using namespace std;

    void show_char(const char * a[]);
    void show_string(string a[]);

    const char* Seasons_char[4] = {"Spring", "Summer", "Autumn", "winter"};
    string Seasons_string[4] = {"Spring", "Summer", "Autumn", "winter"};

    void show_char(const char * a[])
    {
    for (int i = 0; i < 4; i++)
    cout << a[i] << endl;
    }

    void show_string(string a[])
    {
    for (int i = 0; i < 4; i++)
    cout << a[i] << endl;
    }

    int main(int argc, char const *argv[])
    {
    show_char(Seasons_char);
    show_string(Seasons_string);
    return 0;
    }

  创建字符串数组的两种形式。第一种形式时,必须要加const。相当于二维数组char Seasons_char[][4]。

  1. 函数与结构体/结构体指针
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    #include <iostream>
    using namespace std;

    struct box
    {
    char maker[40];
    float height;
    float width;
    float length;
    float volume;
    };

    void show(box x)
    {
    cout << "maker: " << x.maker << endl;
    cout << "height: " << x.height << endl;
    cout << "width: " << x.width << endl;
    cout << "length: " << x.length << endl;
    cout << "volume: " << x.volume << endl;
    }

    void show_pointer(box * x)
    {
    x->volume = x->height * x->length * x->width;
    cout << "maker: " << x->maker << endl;
    cout << "height: " << x->height << endl;
    cout << "width: " << x->width << endl;
    cout << "length: " << x->length << endl;
    cout << "volume: " << x->volume << endl;
    }

    void show(box x);
    void show_pointer(box * x);

    int main(int argc, char const *argv[])
    {
    box box1 = {"cxx", 1, 1, 1.2};
    show(box1);
    show_pointer(&box1);
    return 0;
    }

  在C++中,结构体struct和整型int类似,也是一种数据类型,只不过是由用户自己定义的,所以可以将其看作是int型。
  上面代码创建了2个函数,一个是按值传递结构体,一个是按地址传递结构体。在使用结构体时要先定义一个结构体模板。
  按值传递的结构体中,函数头是void show(box x);表明其数据类型是box,而box就是定义好的结构体类型,相当于int x。在按值传递时,通过用点运算符访问成员变量,调用函数时,传递的参数为结构体的变量名,而按地址传递时,函数头是void show_pointer(box * x);表明其数据类型也是box,但是传递的是一个指针,也就是结构体变量的地址,在按地址传递时,需要用->运算符访问成员变量,调用函数时,传递的参数应该是结构体变量的地址,即在变量名前面加上取地址符。

  1. 函数调用时,传递的参数要么是变量名,要么是变量的地址,不需要加修饰符。

  2. 与普通的变量一样,函数也有地址。函数的地址是存储其机器语言代码的内存的开始地址。函数名即函数的地址,后面的括号表示调用,不需要加取地址符。这样就可以将函数名作为一个参数传递给另一个函数。

    1
    2
    3
    double pam(int);    //函数声明
    double (*pf)(int); //函数指针
    double * pf(int); //指针函数

  声明一个函数pam,有1个参数为int,返回值为double。用一个指针(*pf)来替代函数名就可以声明一个函数指针,也就是必须要指针函数的参数类型和返回值类型。此时pam和*pf一样,都是函数名,而pf就是函数指针,即函数的地址。
  指针必须用括号括起来,否则pf会和后面的括号先结合,然后和*结合,变成指针函数,相当于创建一个pf(int)的函数,其返回值为double类型的指针。

未完待续…

谢谢老板!
-------------本文结束感谢您的阅读给个五星好评吧~~-------------