C++复习笔记

好多东西都忘了,现在重新复习一遍,把遇到的要点都记录下来。随时更新。 # 指针 - C保证在为数组分配存储空间的时候,**指向**数组之后的第一个位置的**指针**也是合法的。也就是说保证**指针** `a + SIZE` 是合法的,但是不保证 `a[SIZE]` 合法. - `const double * p` 为指向常量的指针,`double * const p` 为常量指针. - 多维数组实际上一维的,可以用指针以一维的方式进行遍历。 `z[0]` 、 `z[1]` **指向的是一个包含两个int值的数组** ![](http://images.cnblogs.com/cnblogs_com/xz816111/786501/o_2016-03-29%2021128.jpg) - `int (* p) [2]` p是**一个 指向包含2个int值的数组 的指针** - 允许把非常量指针赋值给常量指针,但是前提是只进行一层间接运算。在进行两层间接运算时,可能会发生`const **p -> p -> const * p`,即 `常量指针指向非常量指针,非常量指针又指向常量指针,这时候就能用非常量指针改变常量的值`. - 声明多维数组的指针时,除了最左边的方括号可以留空之外,其它都需要填写数值。因为首方括号表示这是一个指针,而其它方括号描述的是所指向对象的数据类型。 - `void (* pf)(char *)` `pf`是一个指向函数的指针,可以用 `(*pf)("test")` 这种方式,因为 `pf` 是指向函数的指针,所以 `*pf` 就是访问函数;也可以用 `pf("test")` 的方式,因为函数名本身就是一个指针。如今二者都可以用。 - 数组名是一个指针常量。只有在两种场合下,数组名才不用指针常量来表示:一是当数组名作为`sizeof`操作符或单目操作符`&`的操作数时。`sizeof`返回的是整个数组的长度,而不是指针的长度。取一个数组名的地址产生的是一个指向数组的指针,而不是一个指向某个指针常量的指针。 - 声明一个数组时,编译器将根据声明所指定的元素数量为数组保留内存空间,然后再创建数组名,它的值是一个常量,指向这段空间的起始位置。声明一个指针变量时,编译器只为指针本身保留内存空间,它并不为任何整形值分配内存空间。因此,在执行`int a[5];int * b;`的声明后,`*a` 是完全合法的,但是`* b`将访问内存中某一个不确定的位置。另一方面,表达式`b ++`可以通过编译,但是`a ++`却不行,因为`a`的值是一个常量。 - C函数的所有参数都是**按值传递**,指针参数实际上也是通过按值方式传递,函数得到的是该指针的一份拷贝。 - 将一维数组作为函数参数时,之所以无需注明元素数目,是因为函数并不为数组参数分配内存空间,形参只是一个指针,它指向的是已经在其他地方分配好内存的空间。

  • 默认构造函数(只能有一个):
    • 不带参数的默认构造函数。如Test()
    • 带有参数且所有参数都有默认值的构造函数。如Test(int = 1,double = 2)

如果二者都有的话,会产生二义性,比如Test one,此时匹配上方任意一个都可以,因此只能二选一。

  • 复制构造函数(默认为Class_name(const Class_name &)
    • 当新建一个对象,并将其初始化为同类现有对象时,复制构造函数将被调用
    • 每当程序生成了对象的副本时,编译器都将使用复制构造函数。具体地说,当函数按值传递对象或者返回对象时,都将使用复制构造函数。按值传递意味着创建原始变量的一个副本。

由于按值传递对象将调用复制构造函数,因此应该按引用传递对象。这样可以节省调用构造函数的时间以及存储新对象的空间。

  • 赋值运算符

    • 将已有对象赋给另一个对象时,重载的赋值运算符将被调用
  • 初始化列表

    • Class::Class(int n,int m) : mem_1(n),mem_2(m)
    • 采用这种方式时,初始化工作是在对象创建时完成的,而不是之后
    • 这种格式只适用于构造函数
    • 必须用这种格式来初始化非静态const数据成员引用数据成员
  • 更好的选择:

    • C++ 11 允许以下面这种方式进行初始化,与使用初始化列表等价
    class   my_class
    {
        int mem_1 = 10;
        int mem_2 = 20;
        // ...
    }
    
  • 只要类方法不修改调用对象,就应该将其声明const

  • 派生类

    • 派生类不能直接访问基类的私有成员,必须通过基类的方法进行访问
    • 派生类的构造函数:
      • 派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数
      • 派生类构造函数初始化派生类新增的数据成员
    • 派生类的构造函数总是要调用一个基类的构造函数
  • 继承

    • 基类指针或引用可以指向派生类,但是不能反过来
    • 使用引用或继承时,如果使用vitual,程序将根据引用或指针指向的对象的类型来选择方法。如果没有使用virtual,程序将根据引用或指针类型来选择方法。
    • 在基类方法的声明中使用virtual可使该方法在基类以及所有的派生类(包括从派生类派生出来的类)中是虚的。
    • 构造函数不能是虚函数,析构函数应该是虚函数,友元不能是虚函数。
    • 重新定义继承的方法并不是重载,而是覆盖掉原方法。
    • 如果在派生类中重新定义了基类几个重载函数当中的一个版本,则其它版本将被隐藏,派生类对象无法使用它们,因此如果要在派生类中重新定义,就应该重新定义所有的基类版本。
  • 虚函数与纯虚函数

    • 虚函数:使用引用或继承时,程序将根据引用或指针指向的对象的类型来选择方法。
      • 基类的析构函数应该是虚的,不然将只会调用基类的析构函数而不会调用派生类的。
      • 构造函数不能是虚函数,没有任何意义,派生类总是会先调用基类的构造函数,然后又调用自己的构造函数。
    • 纯虚函数:可以不实现,主要用在基类上来进行派生。
  • 虚函数的实现机制

    • 编译器会给每个对象添加一个隐藏成员,隐藏成员中保存了一个指向函数地址数组的指针,这个数组叫做虚函数表,其中保存了这个类里各个虚函数的地址





原文地址:https://www.cnblogs.com/xz816111/p/5336834.html