• 一篇搞懂C++(万字总结)


    一、基础部分

    1、C++编写步骤

    编写一个C++程序总共分为4个步骤:创建项目->创建文件->编写代码->运行程序

    1. #include
    2. using namespace std;
    3. int main()
    4. {
    5. cout<<"hello world"<
    6. return 0;
    7. }

    2、注释

    1)单行注释:通常放在一行代码的上方,或一条语句的末尾,对该行代码说明。

    //描述信息

    2)多行注释:通常放在一段代码的上方,对该段代码做整体说明。

    /*描述信息*/

    3、变量

    作用:给一段指定的内存空间起名,方便操作这段内存。

    语法:数据类型    变量名=初始值;

    1. #include
    2. using namespace std;
    3. int main()
    4. {
    5. int a=10;
    6. cout<<"a= "<
    7. return 0;
    8. }

    4、常量

    作用:用于记录程序中不可更改的数据。

    1)#define 宏常量:通常在文件上方定义,表示一个常量。

    #define 常量名 常量值

    2)const修饰的变量:通常在变量定义前加关键字const,修饰该变量为常量,不可修改。

    const 数据类型 常量名 = 常量值

    5、关键字

    关键字是C++中预先保留的单词(标识符)在定义变量或常量时,不要使用关键字。

    sizeof关键字

    作用:利用sizeof关键字可以统计数据类型所占内存大小

    语法:sizeof(数据类型/变量)

    1. #include
    2. using namespace std;
    3. int main() {
    4. cout << "short类型所占内存空间为: " << sizeof(short) << endl;
    5. cout << "int类型所占内存空间为: " << sizeof(int) << endl;
    6. cout << "long类型所占内存空间为: " << sizeof(long) << endl;
    7. cout << "long long类型所占内存空间为: " << sizeof(long long) << endl;
    8. return 0;
    9. }

    6、标识符命名规则

    1)标识符不能是关键字;
    2)标识符只能由字母,数字,下划线组成;
    3)第一个字符必须为字母或下划线;
    4)标识符中字母区分大小写。

    7、数据类型

    C++规定在创建一个变量或常量时,必须要指出对应的数据类型,否则无法给变量分配内存。

    转义字符

    作用:用于表示一些不显示出来的ASCII字符

    常用转义字符:     \n(换行)        \\(反斜线)        \t(水平制表)

    数据的输入

    作用:用于从键盘获取数据

    关键字:cin

    语法:cin >> 变量

    7.1、整型

    作用:整型变量表示的是整数类型的数据。

    数据类型                            占用空间                    
    short(短整型)                2字节    
    int(整型)                              4字节
    long(长整型)                  Windows:4字节,Linux:4字节(32位)8字节(64位)
    long long(长长整型)      8字节

    整型大小比较:short < int <= long <= long long

    7.2、浮点型

    作用:用于表示小数。

    浮点类型数据变量分为两种:1)单精度 float 2)双精度 double

    1. float f1 = 3.14f;
    2. double d1 = 3.14;

    两者的区别在于表示的有效数字范围不同:

    数据类型            占用字节        有效数字范围
    float                    4字节            7位有效数字
    double                8字节            15-16位有效数字    

    默认情况下,输出一个小数,会显示出6位有效数字。

    7.3、字符型

    作用:字符型变量用于显示单个字符。

    语法:char ch = ‘a';

    注意:    1)在显示字符串变量时,用单引号将字符括起来,不要用双引号;

                    2)单引号内只能有一个字符,不可以是字符串。

    C和C++中字符型变量只占用一个字节。

    字符变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放到存储单元。

    7.4、字符串类型

    作用:用于表示一串字符。

    C风格字符串:char 变量名[ ] = “字符串”;

    C++风格字符串:string 变量名 = “字符串值”;(使用C++风格字符串时候,要包含#include 这个头文件)

    7.8、布尔类型

    作用:布尔数据类型代表真或假的值。

    bool类型只有两个值:true—真(本质是1) flase—假(本质是0)

    bool类型占1个字节大小。

    8、运算符

    作用:用于执行代码的运算。

    运算符类型                            作用                                                                    运算符
    算术运算符                            用于处理四则运算                        +     -      *      /      %      ++      --
    赋值运算符                            用于将表达式的值赋给变量         =     +=      -=      *=      /=      %=
    比较运算符                            用于表达式的比较,并返回一个真值或假值             ==
    逻辑运算符                            用于根据表达式的值返回真值或假值        !(与)  &&(与)   ||(或)

    9、程序流程结构

    C/C++支持最基本的三种程序运行结构:顺序结构、选择结构、循环结构。

    • 顺序结构:程序按顺序执行,不发生跳转;

    • 选择结构:依据条件是否满足,有选择的执行相应功能;

    • 循环结构:依据条件是否满足,循环多久执行某段代码。

    9.1、选择结构

    1)if语句

    作用:执行满足条件的语句

    单行格式if语句:

    1. if(条件){
    2. 条件满足执行的语句
    3. };

    多行格式if语句:

    1. if(条件){
    2. 条件满足执行的语句
    3. }
    4. else{
    5. 条件不满足执行语句
    6. };

    多条件的if语句:

    1. if(条件1){
    2. 条件1满足执行的语句
    3. }
    4. else if(条件2){
    5. 条件2满足执行的语句
    6. }
    7. else{
    8. 都不满足执行的语句
    9. }

    2)嵌套if语句

    在if语句中,可以嵌套使用if语句,达到更精准的条件判断。

    3)三目运算符

    作用:通过三目运算符实现简单的判断

    语法:表达式1 ?表达式2:表达式3

    如果表达式1的值为真,执行表达式2,并返回表达式2的结果;

    如果表达式1的值为假,执行表达式3,并返回表达式3的结果。

    4)switch语句

    作用:执行多条件分支语句。

    1. switch(表达式)
    2. {
    3. case结果1
    4. 执行语句;
    5. break
    6. case结果2:
    7. 执行语句;
    8. break
    9. ...
    10. default:
    11. 执行语句;
    12. break
    13. }

    9.2、循环结构-while

    作用:满足循环条件,执行循环语句。

    1. while(循环条件){
    2. 循环语句
    3. };

    9.3、do-while循环语句

    作用:满足循环条件,执行循环语句。

    1. do{
    2. 循环语句
    3. }
    4. while(循环条件);

    注意:与while的区别在于do-while会先执行一次循环语句,再判断循环条件。

    1. //在屏幕输出0-9
    2. #include
    3. using namespace std;
    4. int main() {
    5. int num = 0;
    6. do
    7. {
    8. cout << num << endl;
    9. num++;
    10. } while (num<10);
    11. return 0;
    12. }
    1. //水仙花数,每个位数上的数字的3次幂之和等于它本身
    2. #include
    3. using namespace std;
    4. int main() {
    5. int num = 100;//要判断的三位数
    6. int a = 0; //个位
    7. int b = 0; //十位
    8. int c = 0; //百位
    9. do {
    10. a = num % 10; //获取该三位数的个位
    11. b = num / 10 % 10; //获取该三位数的十位
    12. c = num / 100; //获取该三位数的百位
    13. if (a * a * a + b * b * b + c * c * c == num) {
    14. cout << num << "是水仙花数" << endl;
    15. }
    16. num++;
    17. } while (num < 1000);
    18. system("pause");
    19. return 0;
    20. }

    9.4、for循环

    作用:满足循环条件,执行循环语句。

    1. for(起始表达式;条件表达式;末尾循环体){
    2. 循环语句;
    3. }

    注意:for循环体中的表达式,需要用分号进行分隔。

    9.5、嵌套循环

    作用:在循环中再嵌套一层循环,解决一些实际问题。

    1. //乘法口诀表
    2. #include
    3. using namespace std;
    4. int main() {
    5. for (int i = 1; i < 9; i++)
    6. {
    7. for (int j = 0; j <= i; j++)
    8. {
    9. cout << j << "*" << i << "=" << j * i << " ";
    10. }
    11. cout << endl;
    12. };
    13. return 0;
    14. }

    9.6、跳转语句—break语句

    作用:用于跳出选择结构或循环结构

    break使用时机:

    • 出现在switch语句中,用于终止case并跳出switch;

    • 循环语句中,跳出当前的循环语句;

    • 嵌套语句中,跳出最近的内层循环。

    9.7、跳转语句—continue语句

    作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环。

    注意:continue并没有使整个循环终止,而break会跳出循环。

    10、数组

    所谓数组,就是一个集合,里面存放了相同类型的数据元素。

    特点:1)数组中每个数据元素都是相同的数据类型;

    2)数组是由连续的内存位置组成的。

    10.1、一维数组

    一维数组定义方式:
    数据类型    数组名[数组长度];
    数据类型    数组名[数组长度] = {值1,值2,...}
    数据类型    数组名[] = {值1,值2,...}

    1. #include
    2. using namespace std;
    3. int main() {
    4. int arr[5];
    5. int arr2[5] = { 10,20,30,40,50 };
    6. //利用循环输出数组中的元素
    7. for (int i = 0; i < 5; i++)
    8. {
    9. cout << arr2[i] << endl;
    10. }
    11. return 0;
    12. }

    数组名的命名规范与变量名命名规范一致,不要和变量名重名。

    数组中下标是从0开始索引。

    一维数组数组名用途:

    • 可以统计整个数组在内存中的长度 => sizeof(arr)

    • 可以获取数组在内存中的首地址

      1. cout<<"数组首地址为:"<< (int)arr << endl;
      2. cout<<"数组中第一个元素地址为:"<< (int)&arr[0] << endl;

    10.2、二维数组

    二维数组就是在一维数组上,多加一个维度。

    4种定义方式:
    数据类型    数组名[行数][列数];
    数据类型    数组名[行数][列数] = {{数据1,数据2},{数据3,数据4}};
    数据类型    数组名[行数][列数] = {数据1,数据2,数据3,数据4};
    数据类型    数组名[][列数] = {数据1,数据2,数据3,数据4};

    二维数组名组名用途:1)查看二维数组所占内存空间;2)获取二维数组首地址

    11、函数

    作用:将一段经常使用的代码封装起来,减少重复代码。

    11.1、函数的定义

    函数定义的步骤:

    返回值类型 -> 函数名 -> 参数列表 -> 函数体语句 -> return表达式

    1. 语法:
    2. 返回值类型 函数名(参数列表)
    3. {
    4. 函数体语句;
    5. return表达式;
    6. }
    1. int add(int num1,int num2){
    2. int sum = num1 + num2;
    3. return sum;
    4. }

    返回值类型:一个函数可以返回一个值,在函数定义中。

    函数名:给函数起的名称。

    参数列表:使用该函数时,传入的数据。

    函数体语句:花括号内的代码,函数内需要执行的语句。

    return表达式:和返回值类型挂钩,函数执行完后,返回相应的数据。

    11.2、函数的调用

    功能:使用定义好的函数。

    语法:函数名(参数)

    11.3、值传递

    所谓值传递,就是在函数调用时实参将数值传入给形参。

    值传递时,如果形参发生改变,并不会影响实参。

    1. void swap(int num1,int num2){
    2. int temp = num1;
    3. num1 = num2;
    4. num2 = temp;
    5. cout <<"交换后"<
    6. cout<<"num1 = "<< num1 << endl;
    7. cout<<"num2 = "<< num2 << endl;
    8. }

    11.4、函数的常见形式

    无参无返 有参无返 无参无返 有参有返

    11.5、函数的声明

    作用:告诉编译器函数名称及如何调用函数,函数主体可以单独定义。

    函数的声明可以多次,但函数的定义只能有一次。

    11.6、函数的分文件编写

    作用:让代码结构更加清晰。

    函数分文件编写一般步骤:
    1)创建后缀名为.h的头文件;
    2)创建后缀名为.cpp的源文件;
    3)在头文件中写函数的声明;
    4)在源文件中写函数的定义。

    12、指针

    作用:可以通过指针间接访问内存

    内存编号是从0开始记录的,一般用十六进制数字表示。

    可以利用指针变量保存地址。

    12.1、指针定义和使用

    语法:数据类型 * 指针变量名

    1. //定义指针
    2. int a = 10;
    3. int *p;
    4. //让指针记录变量a的地址
    5. p = &a;

    使用指针:可以通过引用的方式来找到指针指向的内存。

    *p; //指针前加*代表解引用,找到指针指向的内存中的数据

    指针所占内存的空间:32位占用4个字节空间; 64位占用8个字节空间

    12.2、空指针

    空指针:指针变量指向内存中编号为0的空间

    用途:初始化指针变量

    注意:空指针指向的内存是不可以访问的。

    1. //指针变量p指向内存地址编号为0的空间
    2. int *p = NULL;
    3. //访问空指针报错,内存编号0-255为系统占用内存,不允许用户访问。
    4. cout << *p << endl;

    12.3、const修饰指针

    const修饰指针的三种情况:
        const修饰指针    ——常量指针
        const修饰常量 ——指针常量
        const即修饰指针,又修饰常量

    常量指针特点:指针的指向可以修改,但指针指向的值不可以改。

    1. int a = 10;
    2. int b = 10;
    3. const int *p = &a;
    4. *p = 20;//错
    5. p = &b;//对

    指针常量特点:指针的指向不可以改,指针指向的值可以改。

    1. int a = 10;
    2. int b = 10;
    3. int *const p = &a;
    4. *p = 20;//对
    5. p = &b;//错

    const即修饰指针,又修饰常量:指针的指向和指针指向的值都不可以改。

    技巧:看const右侧紧跟的是指针还是常量,是指针就是常量指针,是常量就是指针常量。

    13、结构体

    结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。

    1. 语法:
    2. struct 结构体名{
    3. 结构体成员列表
    4. };

    通过结构体创建变量的方式有3种:

    • struct 结构体名 变量名

    • struct 结构体名 变量名 = {成员1值,成员2值,...}

    • 定义结构体时顺便创建变量

    1. struct Student{
    2. string name;
    3. int age;
    4. int score;
    5. }
    6. struct Student s1;
    7. //给s1赋值,通过.访问结构体变量中的属性
    8. s1.name = "张三";
    9. s1.age = 18;
    10. s1.score = 100;
    11. struct Student s2 = {"李四",19,80};
    1. struct Student{
    2. string name;
    3. int age;
    4. int score;
    5. }s3;
    6. s3.name = "王五";
    7. s3.age = 20;
    8. s3.score = 60;

    总结: 定义结构体时的关键字是struct,不可省略;

    结构体变量利用操作符“.”访问成员。

    struct和class的区别:在C++中struct和class唯一的区别在于默认的访问权限不同。struct默认权限为公有,class默认私有。

    13.1、结构体数组

    作用:将自定义的结构体放到数组中方便维护。

    语法:struct 结构体名 数组名[元素个数] = {{},{},{},...}

    1. struct Student{
    2. string name;
    3. int age;
    4. int score;
    5. }
    6. //创建结构体数组
    7. struct Student stuArr[3] = {{"刘"22100},{"陈"22100},{"池",22,100}};

    13.2、结构体指针

    作用:通过指针访问结构体中的成员。

    利用操作符“ -> ”可以通过结构体指针访问结构体属性。

    1. //1、创建学生结构体
    2. struct Student s = {"liu",22,100};
    3. //2、通过指针指向结构体
    4. struct Student *p = &s;
    5. //3、通过指针访问结构体变量中的数据
    6. cout <<"姓名是:"<< p->name <

    13.3、结构体嵌套结构体

    作用:结构体中的成员可以是另一个结构体。

    1. //每个老师辅导一个成员,一个老师的结构体中,记录一个学生的结构体
    2. struct Student{
    3. string name;
    4. int age;
    5. int score;
    6. };
    7. struct Teacher{
    8. int id;
    9. string name;
    10. int age;
    11. struct Student stu;//子结构学生
    12. };
    13. //创建老师
    14. int main(){
    15. Teacher t;
    16. t.id = 1000;
    17. t.name = "wu";
    18. t.age = 40;
    19. t.stu.name = "liu";
    20. t.stu.age = 22;
    21. t.stu.score = 80;
    22. }

    13.4、结构体做函数参数

    作用:将结构体作为参数向函数中传递

    传递方式: 值传递 地址传递

    1. struct Student{
    2. string name;
    3. int age;
    4. int score;
    5. };
    6. //值传递
    7. void printStudent(student stu){
    8. stu.age = 22;
    9. cout<<"子函数中 年龄:"<< stu.age << endl;
    10. }
    11. //地址传递
    12. void printStudent2(struct Student *p){
    13. cout<<"年龄:"<< p->age <
    14. }
    15. void printStudent(s);//值传递
    16. void printStudent2(&s);//地址传递

    如果不想修改主函数中的数据,用值传递,反之用地址传递。

    二、提高部分

    1、内分区模型

    C++程序在执行时,将内存大方向划分为4个区域。

    代码区:存放函数体的二进制代码,由操作系统进行管理。
    全局区:存放全局变量和静态变量以及常量。
    栈区:由编译器自动分配释放,存放函数的参数值,局部变量等。
    堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

    1.1、程序运行前

    在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域:

    1)代码区:存放CPU执行的机器指令。代码区是共享的,只读的。

    2)全局区:存放全局变量、静态变量、常量。该区域的数据在程序结束后由操作系统释放。

    1.2、程序运行后

    1)栈区:由编译器自动分配释放,存放函数的参数值,局部变量等。

    2)堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

    2、引用

    作用:给变量起别名。

    语法:数据类型 &别名 = 原名

    1. int a = 10;
    2. int &b = a;
    3. b = 20;
    4. cout << a << endl; //输出也为20

    注意:1)引用必须初始化;2)引用在初始化后,不可以改变。

    1. int a = 10;
    2. int b = 20;
    3. //int &c; //错误,必须初始化
    4. int &c = a;//一旦初始化,就不可以更改
    5. c = b;//这是赋值操作,不是更改引用

    引用的本质:引用的本质在C++内部实现是一个指针常量。

    2.1、引用做函数参数

    作用:函数传参时,可以利用引用让形参修饰实参。

    1. //1、值传递,形参不会修饰实参
    2. void mySwap1(int a,int b){
    3. int temp = a;
    4. a = b;
    5. b = temp;
    6. }
    7. //2、地址传递,形参修饰实参
    8. void mySwap2(int *a,int *b){
    9. int temp = *a;
    10. *a = *b;
    11. *b = temp;
    12. }
    13. //3、引用传递,形参会修饰实参
    14. void mySwap3(int &a,int &b){
    15. int temp = a;
    16. a = b;
    17. b = temp;
    18. }
    19. //总结:通过引用参数产生的效果同按地址传递是一样的。引用的语法更加清晰。

    3、函数提高

    3.1、函数默认参数

    语法: 返回值类型 函数名(参数 = 默认值){}

    1. int func(int a, int b = 10, int c =10){
    2. return a + b + c;
    3. }

    注意: 1)如果某个函数参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值;

    2)如果函数声明有默认值,函数实现的时候就不能有默认参数。

    3.2、函数重载

    作用:函数名可以相同,提高复用性。

    函数重载满足条件:1)同一个作用域下;2)函数名相同;3)函数参数类型不同或个数不同或顺序不同

    4、封装

    C++面向对象三大特性:封装、继承、多态。C++认为万事万物都皆为对象,对象上有其属性和行为。

    封装的意义:1)将属性和行为作为一个整体,表现生活中的事物
                        2)将属性和行为加以权限控制

    封装意义一:在设计类的时候,属性和行为写在一起,表现事物。

    语法:class 类名{访问权限:属性/行为};

    1. //设计一个圆类,求圆的周长
    2. const double PI = 3.14;
    3. class Circle{
    4. public: //公共权限
    5. int m_r; //属性-半径
    6. deouble calculatez(){
    7. return 2 * PI * m_r; //行为-获取圆的周长
    8. }
    9. };
    10. int main(){
    11. Circle c1; //通过圆类,创建具体的圆(对象)
    12. c1.m_r = 10; //给圆对象的属性进行赋值
    13. cout <<"圆的周长:"<< c1.calculatez() << endl;
    14. }

    类中的属性和行为,统一称为成员。

    属性:成员属性,成员变量。 行为:成员函数,成员方法。

    封装意义二:类在设计时,可以把属性和行为放在不同的权限下,加以控制。

    三种权限:
    公共权限    public            类内可以访问    类外可以访问
    保护权限    protected        类内可以访问    类外不可以访问
    私有权限    private            类内可以访问    类外不可以访问

    成员属性设置为私有优点:

    • 将所有成员属性设置为私有,可以自己控制读写权限;

    • 对于写权限,我们可以检测数据的有效性

    5、构造函数和析构函数

    5.1、构造函数

    主要作用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

    语法:类名(){}

    • 构造函数没有返回值也不写void;

    • 函数名称与类名相同;

    • 构造函数可以有参数,因此可以发生重载;

    • 程序在调用对象时会自动调用构造,无须手动调用,而且只会调用一次。

    5.2、析构函数

    主要作用于对象销毁前系统自动调用,执行一些清理工作。

    语法: ~类名(){}

    • 析构函数没有返回值也不写void;

    • 函数名称与类名相同,在名称前面加上符号~;

    • 析构函数不可以有参数,因此不能发生重载;

    • 程序在对象销毁前会自动调用析构函数,无须手动调用,而且只会调用一次。

    1. class Person{
    2. public:
    3. Person(); //构造函数
    4. {
    5. cout <<"Person构造函数调用"<< endl;
    6. }
    7. ~Person(); //析构函数
    8. {
    9. cout <<"Person析构函数调用" << endl;
    10. }
    11. }

    5.3、构造函数的分类及调用

    两种分类方式:
    1)按参数分为:有参构造和无参构造
    2)按类型分为:普通构造和拷贝构造

    三种调用方式:1)括号法; 2)显示法; 3)隐式转换法

    1. //拷贝构造函数
    2. Person(const Person &p){
    3. age = p.age; //将传入的人身上的所有属性拷贝到我身上
    4. }
    5. //调用
    6. Person p1; //默认构造函数调用 注意:调用默认参数时,不要加()
    7. Person p2; //有参构造函数
    8. Person p3; //拷贝构造函数
    9. //显示法
    10. Person p2 = Person(10); //有参构造
    11. Person p3 = Person(p2); //拷贝构造 注意:不要利用拷贝构造函数,初始化匿名对象
    12. //隐式转换法
    13. Person p4 = 10; //相当于Person p4 = Person(10);

    5.4、拷贝构造函数调用时机

    C++中拷贝构造函数调用时机通常有三种情况:

    • 使用一个已经创建完毕的对象来初始化一个新对象;

    • 值传递的方式给函数参数传值;

    • 以值方式返回局部对象。

    5.5、深拷贝与浅拷贝

    浅拷贝:简单的赋值拷贝操作

    深拷贝:在堆区重新申请空间,进行拷贝操作。

    浅拷贝带来的问题:堆区的内存重复释放。

    浅拷贝的问题要利用深拷贝进行解决:

    m_Height = new int(*p.m_Height);

    总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。

    5.6、初始化列表

    作用:C++提供了初始化列表语法,用来初始化属性。

    语法:构造函数():属性1(值1),属性2(值2)...{}

    1. class Person{
    2. public:
    3. Person(int a,int b,int c){
    4. m_a = a;
    5. m_b = b;
    6. m_c = c;
    7. }
    8. }
    9. //初始化列表赋初值
    10. Person():m_a(10), m_b(20), m_c(30){}

    5.7、静态成员

    静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。

    静态成员分为:
    1)静态成员变量:所有对象共享同一份数据;在编译阶段分配内存;类内声明,类外初始化。
    2)静态成员函数:所有对象共享同一个函数;静态成员函数只能访问静态成员变量。

    5.8、const修饰成员函数

    1)常函数:成员函数后加const后我们称这个函数为常函数

    常函数内一般不可修改成员属性,成员函数声明时加关键字mutable后,在常函数中依然可以修改。

    2)常对象:声明对象前加const称该对象为常对象。

    常对象只能调用常函数。

    6、友元

    友元的目的是让一个函数或类访问另一个类中的私有成员。

    友元的关键字:friend

    友元的三种实现:1)全局函数做友元;2)类做友元;3)成员函数做友元

    1. //1、全局函数做友元
    2. class Building{
    3. friend void goodGay(Buliding *buliding);
    4. }
    5. //2、类做友元
    6. class Building{
    7. friend class GoodGay; //GoodGay类是本类的好朋友,可以访问本类中私有成员
    8. }
    9. //3、成员函数做友元
    10. class Building{
    11. friend void GoodGay::visit();
    12. ...
    13. }

    7、继承

    继承的好处:减少重复代码。

    语法:class 子类:继承方式 父类

    子类也称为派生类;父类也称为基类。

    派生类的成员,包含两大部分:一类是从基类继承过来的;一类是自己增加的成员。

    从基类继承过来的表现其共性,新增成员体现其个性。

    7.1、继承方式

    继承方式一共有3种:公共继承;保护继承;私有继承

    父类所有非静态成员属性都会被子类继承下去。

    7.2、继承中构造和析构顺序

    子类继承父类后,当创建子类对象,也会调用父类的构造函数。

    继承中构造和析构函数顺序:先构造父类,再构造子类,析构顺序与构造顺序相反。

    7.3、继承同名成员处理方式

    访问子类同名成员:直接访问即可。

    s.m_a

    访问父类同名成员:需要加作用域。

    s.Base::m_a

    小结:1)子类对象可以直接访问子类中同名成员;

    2)子类对象加作用域可以访问到父类同名成员;

    3)当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数。

    7.4、多继承

    C++允许一个类继承多个类。

    语法:class 子类:继承方式 父类1,继承方式 父类2,...

    多继承可能会引发父类中有同名成员出现,需要加作用域区分。实际开发不建议使用多继承。

    8、多态

    多态定义: 同一种行为(调用)导致的不同的结果。

    虚函数:虚函数是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。

    1. #include
    2. using namespace std;
    3. class Shape {
    4. protected:
    5. int width, height;
    6. public:
    7. Shape( int a=0, int b=0)
    8. {
    9. width = a;
    10. height = b;
    11. }
    12. virtual int area()
    13. {
    14. cout << "Parent class area :" <
    15. return 0;
    16. }
    17. };
    18. class Rectangle: public Shape{
    19. public:
    20. Rectangle( int a=0, int b=0):Shape(a, b) { }
    21. int area ()
    22. {
    23. cout << "Rectangle class area :" <
    24. return (width * height);
    25. }
    26. };
    27. class Triangle: public Shape{
    28. public:
    29. Triangle( int a=0, int b=0):Shape(a, b) { }
    30. int area ()
    31. {
    32. cout << "Triangle class area :" <
    33. return (width * height / 2);
    34. }
    35. };
    36. // 程序的主函数
    37. int main( )
    38. {
    39. Shape *shape;
    40. Rectangle rec(10,7);
    41. Triangle tri(10,5);
    42. // 存储矩形的地址
    43. shape = &rec;
    44. // 调用矩形的求面积函数 area
    45. shape->area();
    46. // 存储三角形的地址
    47. shape = &tri;
    48. // 调用三角形的求面积函数 area
    49. shape->area();
    50. return 0;
    51. }

    多态分类:1)静态多态:函数重载和运算符重载都属于静态多态,复用函数名;

    2)动态多态:派生类和虚函数实现运行多态。

    静态多态和动态多态的区别:

    • 静态多态的函数地址早绑定——编译阶段确定函数地址;

    • 动态多态的函数地址晚绑定——运行阶段确定函数地址。

    动态多态满足条件:1)有继承关系;2)子类需要重写父类虚函数

    动态多态的使用:父类的指针或指引指向子类对象。

    Base *base = new son;

    重写:函数返回值类型 函数名 参数列表完全一致称为重写。

    多态优点:1)代码组织结构清晰;

    2)可读性强;

    3)利于前期和后期的拓展和维护。

  • 相关阅读:
    Docker搭建redis集群
    校园微社区微信小程序源码/二手交易/兼职交友微信小程序源码
    2. Go的数据类型
    AcWing第80场周赛总结
    postgresql-使用plpgsql批量插入用户测试数据
    【小沐学前端】从零开始搭建一个Vue项目
    混淆技术研究-OLLVM混淆-虚假控制流(BCF)
    2022.11.17补题祭
    ubuntu22.04 安装并使用 DirBuster
    Redis实现消息队列(双端队列的模式,发布订阅模式)
  • 原文地址:https://blog.csdn.net/beiye_/article/details/127348144