C++面向对象语法总结(三)

  • 目录
    • 《C++基础语法总结》
    • 《C++面向对象语法总结(一)》
    • 《C++面向对象语法总结(二)》

二十一、多继承

  • C++允许一个类可以有多个父类(不建议使用,会增加程序设计复杂度)
  • 在多继承中,会按照继承顺序将父类的成员变量放到子类成员变量的前面
    C++面向对象语法总结(三)_第1张图片
  • 多继承体系中,子类调用父类构造函数只需要在初始化列表中调用即可
    C++面向对象语法总结(三)_第2张图片
  • 如果子类继承的多个父类都有虚函数,那么子类对象就会产生对应的多张虚表
    C++面向对象语法总结(三)_第3张图片
  • 如果子类和父类中都有相同的同名函数,直接调用会调用子类中的函数,调用父类中的同名函数需要显式指定父类名称
    C++面向对象语法总结(三)_第4张图片
    C++面向对象语法总结(三)_第5张图片
  • 如果子类和父类都具有同名的成员变量,直接调用会调用子类的成员变量,如果需要调用父类的成员变量,需要显式指定调用的父类
    C++面向对象语法总结(三)_第6张图片

二十二、菱形继承

  • 菱形继承指的是两个类继承了一个超类,然后有一个子类又多继承了这两个类,其继承关系图类似于菱形
    C++面向对象语法总结(三)_第7张图片

  • 菱形继承的问题

    • 最底下的子类从积累继承的成员变量冗余、重复
    • 最底下的子类无法访问基类的成员,有二义性
      C++面向对象语法总结(三)_第8张图片

二十三、虚继承

  • 虚继承指的是在继承的类前面加上virtual关键字
  • 虚继承可以解决菱形继承带来的问题
  • Person类被称为虚基类
    C++面向对象语法总结(三)_第9张图片
  • 虚继承会增加内存中的数据量,在虚继承中,虚表中一般会包含两个偏移量
    • 虚表指针于本类起始的偏移量(一般是0)
    • 虚基类第一个成员变量与本类起始的偏移量
      在这里插入图片描述
  • 虚继承的内存图(32位环境)
    C++面向对象语法总结(三)_第10张图片

二十四、静态成员(static)

  • 静态成员:被static修饰的成员变量或函数

    • 可以通过对象(对象.静态成员)、对象指针(对象指针 -> 静态成员)、类(类::静态成员)来访问静态成员变量或函数
  • 静态成员变量

    • 存储在数据段(全局区,类似于全局变量),整个程序运行过程中只有一份内存
    • 对比全局变量,它可以设定访问权限(public、protected、private),达到局部共享的目的
    • 必须初始化,而且必须在类的外面初始化,初始化时不能带static,如果累的声明和实现分离,需要在实现(cpp文件)中初始化
      C++面向对象语法总结(三)_第11张图片
  • 静态成员函数

    • 内部不能使用this指针(this指针只能用在非静态成员函数内部)
    • 不能是虚函数(虚函数只能是非静态成员函数)
    • 内部不能访问非静态的成员变量和函数,只能访问静态的成员变量和函数
    • 非静态成员函数内部可以访问静态成员变量和函数
    • 构造函数、析构函数不能是静态
    • 当声明和实现分离时,实现部分不能带static
  • 静态成员经典应用——单例模式
    C++面向对象语法总结(三)_第12张图片

二十五、const成员

  • const成员:被const修饰的成员变量、非静态成员函数
  • const成员变量
    • 必须初始化(类内部初始化),可以在声明的时候直接初始化赋值
    • 非static的const成员变量还可以再初始化列表中初始化
  • const成员函数(非静态)
    • const关键字写在参数列表后面,函数的声明和实现都必须带const
    • 内部不能修改非static成员变量
    • 内部只能调用const成员函数,static成员函数
    • 非const成员函数可以调用const成员函数
    • const成员函数和非const成员函数构成重载
    • 非const对象(指针)优先调用非const成员函数
    • const对象(指针)只能调用const成员函数、static成员函数
      C++面向对象语法总结(三)_第13张图片

二十六、引用类型成员

  • 引用类型成员变量必须初始化(不考虑static情况)
    • 在声明的时候直接初始化
    • 通过初始化列表初始化
      C++面向对象语法总结(三)_第14张图片

二十七、拷贝构造函数(Copy Constructor)

  • 拷贝构造函数是构造函数的一种
  • 当利用已存在的对象创建一个新对象(类似于拷贝),就会调用新对象的拷贝构造函数进行初始化
  • 拷贝构造函数格式是固定的,接受一个const引用作为参数
  • 类中默认的拷贝构造函数是直接拷贝已有对象的内存,当对象中有指针变量时,也是拷贝的指针变量的值,而不是指针变量指向的对象,所以是浅拷贝
    C++面向对象语法总结(三)_第15张图片
  • 继承体系中调用父类的拷贝构造函数直接在初始化列表显式调用,继承体系中默认也会直接拷贝父类对象的内存(因为变量的内存是连续的)
    C++面向对象语法总结(三)_第16张图片
  • 拷贝构造函数的使用
    • 下面代码中,car2、car3都是通过拷贝构造函数初始化的,car、car4是通过非拷贝构造函数初始化的
    • 变量前面有类型是新建对象的操作,没有类型是赋值操作,如car4=car3就是复制操作(默认是浅拷贝),并不会调用拷贝构造函数
      C++面向对象语法总结(三)_第17张图片

二十八、浅拷贝、深拷贝

  • 对于指针变量,如果只是拷贝了指针的地址,并没有拷贝指向的具体的值,叫做浅拷贝,如果拷贝了指向的值,就叫做深拷贝
  • 编译器默认提供的拷贝是浅拷贝
    • 将一个对象中所有成员变量的值拷贝到另一个对象(直接拷贝内存中的值)
    • 如果某个成员变量是指针,只会拷贝指针中存储的地址值,并不会拷贝指针指向的内存空间的值
    • 可能会导致堆空间多次free的情况,因为浅拷贝拷贝的是地址,在拷贝的不同对象中free内存,实际上释放的是同一个内存空间
  • 如果需要实现深拷贝,就需要自定义拷贝构造函数
    • 将指针类型的成员变量所指向的内存空间内的值,拷贝到新的内存空间
  • 深拷贝示例
    C++面向对象语法总结(三)_第18张图片

二十九、对象型参数和返回值

  • 使用对象类型作为函数的参数或者返回值,是进行了对象拷贝的,可能会产生一些不必要的中间对象
  • 函数参数默认是拷贝传递,所以作为对象参数,也会拷贝
  • 因为函数栈中的对象调用完会释放,所以作为返回值的时候,编译器必须进行拷贝,原来函数中的对象已经释放了
    C++面向对象语法总结(三)_第19张图片

三十、匿名对象(临时对象)

  • 匿名对象:没有变量名、没有被指针指向的对象,用完后马上调用析构
  • 匿名对象作为实参和返回值时,编译器会做优化,只会产生一个对象,调用一次构造函数
    C++面向对象语法总结(三)_第20张图片

三十一、隐式构造

  • c++ 中存在隐式构造的现象:在某些情况下,会隐式调用单参数的构造函数
    C++面向对象语法总结(三)_第21张图片
  • 可以通过关键字explicit禁止掉隐式构造
    C++面向对象语法总结(三)_第22张图片

三十二、编译器自动生产构造函数的情况

  • c++的编译器在某些特定的情况下,会给类自动生成无参的构造函数,比如

    • 成员变量在声明的同时进行了初始化
    • 有定义虚函数
    • 继承了其他的类
    • 包含了对象类型成员,且这个成员有构造函数(编译器生成或自定义)
    • 父类有构造函数(编译器生成或自定义)
  • 对象创建后,需要做一些额外的操作时(比如内存操作、函数调用),编译器一般都会为其自动生成无参的构造函数

  • 并不是所有情况都会生成无参的构造函数

三十三、内部类

  • 如果将类A定义在类C的内部,那么类A就是一个内部类(嵌套类)
  • 内部类的特点
    • 支持public、protected、private权限
    • 成员函数可以直接访问其外部类的所有成员(反过来则不行)
    • 成员函数可以直接不带类名、对象名访问其外部类的static成员
    • 不会影响外部类的内存布局
    • 可以在外部类内部声明,在外部类外面进行定义
      C++面向对象语法总结(三)_第23张图片
  • 内部类声明和实现分离的写法
    C++面向对象语法总结(三)_第24张图片

三十四、友元

  • 友元包括友元函数和友元类
  • 如果将函数A(非成员函数)声明为类C的友元,那么函数A就能直接访问类C对象的所有成员
  • 如果将类A声明为类C的友元,那么类A中的所有成员函数都能直接访问类C对象的所有成员
  • 友元破坏了面向对象的封装性,但在某些频繁访问成员变量的地方可以提高性能
  • 友元的声明可以在类的任何位置
    C++面向对象语法总结(三)_第25张图片

三十五、局部类

  • 在一个函数内部定义的类,称为局部类
  • 局部类的特点
    • 作用域仅限于所在函数内部
    • 其所有成员必须定义在类内部,不允许定义static成员变量
    • 成员函数不能直接访问函数的局部变量(static变量除外)
      C++面向对象语法总结(三)_第26张图片

后记
  个人总结,欢迎转载、评论、批评指正

你可能感兴趣的:(C\C++,c++,开发语言)