C++ 复习记录(个人记录)

1、构造函数(constructor)是什么

答:类里面定义一个函数, 和类名一样, 这样在我们生成一个对象之后,就会默认调用这个函数,初始化这个类。

子类B继承父类A的情况, 当你调用子类的对象,构造函数会怎么样运行?

  • 1、创建子类对象时,程序首先调用父类的构造函数,然后再调用子类的构造函数

    • 父类构造函数负责初始化继承的数据成员
    • 子类构造函数主要用于初始化新增的数据成员
  • 2、子类构造函数总是需要调用一个父类构造函数;默认就是调用父类无参数构造函数,如果父类没有这个构造函数,程序会默认生成一个无参数构造的,如果父类又 有参数构造函数,但是没有无参数的构造函数,就会报错。

    • 所以当父类没有无参数的构造函数时,就必须明确指明调用哪一个构造函数,需要用到初始化列表这个方式。
       Person(int age, char *address): age(age), address(address)

2、final用于禁止被重写和继承,确保自己的唯一,是最后一个。

  • 1 、禁止类自己被继承
    final 用于禁止 虚函数被重写和 类被继承 class Person final {} ,然后这个Person就不可以被继承了,
  • 2、 一个类, 自己继承了别人后,禁止别人继承自己。
class Person {};class Student final: public Person {};class Middel_School_Student : public Student{  // 失败,因为父类Student是final修饰的};

就是意味着继承了Person。但是Student不能被别人继承。

  • 3、final用于禁止函数被重写
class Person { virtual void run() final {  // 表示该方法是最终方法,无法被重写  }
};class Student : public Person{// 编译错误,方法无法被重写。void run(){ }
};

final总是放在定义类的后面,和方法的后面, 类是 class A final {}. 和 class A :final public B {}.方法是

class Person {  virtual void C() final {  // 表示该方法是最终方法,无法被重写     }
};

也是在后面,但是在()前。

3、 友元 friend ,目的是为了使用私有属性的函数和变量。 类似一个白名单

如果一个类A里面有私有的方法和属性,当类生成一个对象 a ,肯定是无法调用这些方法和属性的,如果在类A里面,加上 友元函数 或者友元类 之后,这个友元函数内部可以无视private的影响。 友元类里也可以无视private的影响。

5、重载

C++中,一个类里,如果有两个函数名一样的函数,但是入参不同,就属于重载

4、关于 继承

1、继承是什么
继承就是把一个类里的变量和方法 “继承” 给另一个类。如果B类 继承 A类,我们就叫B类是子类, A类是B类的父类,B类有A类的所有功能和方法(先这样粗浅的理解)。

2、继承的写法
继承有三种方式 pubulic prtected private 和类的封装时候的三种方式一样。不写默认就是private

class 子类名:[继承方式]父类名{    子类新增加的成员    
};

2、继承的用法

  • 子类继承父类之后,拥有了一套和父类一样的变量和方法,即可以使用父类的方法和变量的,如果想要使用父类中继承下来的变量和函数。就在子类中使用 this-> 【方法和变量】 ,就可以调用。

3、继承需要注意

  • 类本身是一个抽象的概念,类本身不占用内存空间;它仅仅是一个蓝图或模板,用于创建对象。只有当类被实例化为对象时,才会分配内存空间。每个对象都有其独立的地址。所以当我们用类生成对象时,每个对象都是一个独立的个体。

  • 比如:如果C++中,一个类B,继承于类A, 则类A的对象中的变量var_a, 在类B的对象中也会有,如果类B生成一个对象obj_b, 那么这个对象obj_b里面也有var_a。 这个时候,使用类A的方法调用var_a和 使用类B的方法调用var_a,都可以调用到var_a, 并且这两个var_a是同一个,即地址是一样的。

#include <iostream>using namespace std;class A {
public:int var_a = 4;void get_addr() {cout << "IN A : var_a  ==" << var_a << "\n";cout << "IN A : var_a address ==" << &var_a;}
};class B : public A {
public:int var_b;void get_addr1() {cout << "IN B : var_a  ==" << var_a << "\n";cout << "IN B : var_a address ==" << &(this->var_a) << "\n";}void get_addr2() {A::get_addr();}};int main() {B obj_b;obj_b.var_a = 10;//打印在对象obj_b里, 调用类B变量 var_a的值和地址obj_b.get_addr1();cout<<"================================================="<<"\n";//打印在对象obj_b里, 调用类A变量 var_a的值和地址obj_b.get_addr2();return 0;
}
  • 4、继承之后怎么用( 等重写,多态知道了之后再看)

继承之后,子类的对象,自然可以调用父类的一切(在子类的函数中,调用从父类继承而来的成员变量或成员函数,直接使用this),那么父类的对象,可以调用子类么 ?

  1. 子类对象可以调用父类的方法
    当一个类(子类)继承自另一个类(父类)时,子类对象可以调用从父类继承来的所有公有(public)和保护(protected)成员(包括方法和属性)。如果父类的方法或属性是私有的(private),则子类无法直接访问它们,但可以通过父类的公有或保护方法来间接访问。

  2. 父类对象不能调用子类的方法
    父类对象只能调用自己定义的公有和保护成员,不能调用子类特有的方法或属性。这是因为父类对象在内存中的布局并不包含子类的任何额外成员或方法。换句话说,父类对象对子类扩展的部分一无所知。

  3. 多态性
    虽然父类对象不能直接调用子类的方法,但可以通过指向子类对象的父类指针或引用来实现多态性。这通常是通过在父类中定义虚函数(virtual function)来实现的。当子类重写了父类的虚函数时,通过父类指针或引用调用的将是子类版本的函数。

示例代码:

#include <iostream>class Parent {
public:void parentMethod() {std::cout << "Parent method called" << std::endl;}virtual void virtualMethod() {std::cout << "Parent virtual method called" << std::endl;}
};class Child : public Parent {
public:void childMethod() {std::cout << "Child method called" << std::endl;}void virtualMethod() override {std::cout << "Child virtual method called" << std::endl;}
};int main() {Parent parent;Child child;// 子类对象调用父类方法child.parentMethod();// 子类对象调用自己的方法child.childMethod();// 通过父类指针调用子类重写的虚函数Parent* parentPtr = &child;parentPtr->virtualMethod();  // 输出:Child virtual method called// 父类对象不能调用子类方法// parent.childMethod();  // 编译错误return 0;
}

在这个例子中:

  • child 对象可以调用 parentMethod()childMethod()
  • 通过父类指针 parentPtr 调用 virtualMethod() 时,实际调用的是子类的 virtualMethod(),展示了多态性。
  • 尝试通过父类对象 parent 调用 childMethod() 会导致编译错误,因为父类对象不知道子类的任何扩展。

总结:子类对象可以调用父类的公有和保护成员,但父类对象不能调用子类的成员。多态性允许通过父类指针或引用来间接调用子类的重写方法。

4 、 那么 ,是不是通过父类指针,调用子类的成员,一定是父类也拥有的成员才可以?

是的,通过父类指针调用子类的成员时,只能调用父类也拥有的成员。这是因为多态是基于父类指针或引用来实现的,编译器在编译时期会根据指针或引用的类型来确定要调用的成员函数。如果子类有额外的成员,且这些成员在父类中没有对应的声明,那么通过父类指针或引用是无法调用这些成员的。

5、重写

重写就是子类继承父类之后,子类中,定义了父类相同名字的函数。
重写之后,子类直接调用,就是调用的子类自己的函数。 父类中所有的同名的函数(为啥说所有,是因为父类中可能有重载的函数,所以会有同名的函数 ),都被子类的同名函数覆盖了,即父类中所有的同名函数,子类都不会继承,子类只用自己重写的那个,如果你想在子类中调用这个被你重写的,就需要用父类名去调用 【父类名】::【父类的重写的方法】 。

#include <iostream>using namespace std;class A {
public:void display() {cout << "A::display()" << "\n";}// 1、重载一个display函数void display(int num) {cout << "A::display(int num)" << "\n";}
};class B : public A {
public: // 2、重写一个display. 重写之后,自然而然的,父类的void display() 方法被顶替了,并且 void display(int num)也被顶替了(重载的全被顶替了)// 综上 父类的两个display都不不能直接调用了。void display() {cout << "B::display()" << "\n"; //3、 this->display(100);  // 编译失败,原因如上面解释的一样 ,但是如果想使用父类的函数也是可以的, 方法1,需要用父类,类A来调用方式如下cout <<"---------------------------------------------------------"<< "\n";A::display(); A::display(100); }};int main() { class B b; b.display();  // 调用子类的函数 //4、同2表示的一样 // b.display(100);  // 编译失败 return 0; 
}

在这里插入图片描述

6、 多层继承和多继承
  • 多层继承(Hierarchical Inheritance)就是 继承了 很多层,比如 爷爷继承给父亲,父亲继承给儿子,继承了3层。
  • 多继承(Multiple Inheritance)就是 同时继承了多个, 比如 父亲母亲,同时继承给儿子。
7、 多态

1、多态就是,有多个相同名字的函数,当调用时,会根据调用时的方式不同,调用不同的函数。比如一个人,同样是说话这个行为,对有的人态度好,对有的人态度差,展现了不同的“态度”

2、C++中通过 visual (虚函数)来实现多态。

  • 通过 virtual可以实现真正的多态

    virtual (虚函数 )可以在父类的指针指向子类对象的前提下,通过父类的指针调用子类的成员函数
    这种技术让父类的指针或引用具备了多种形态,这就是所谓的多态

  • 最终形成的功能:

    • 如果父类指针指向的是一个父类对象,则调用父类的函数
    • 如果父类指针指向的是一个子类对象,则调用子类的函数

3、怎么使用 visual

  • 定义 visual ( 虚函数) 非常简单,只需要在函数声明前,加上 virtual 关键字即可
    • 在父类的函数上添加 virtual 关键字,可使子类的同名函数也变成虚函数,可以子类父类都写上visual
#include<iostream> using namespace std; class Father { 
public: virtual void show() { cout << "father show" << endl; }
};class Children : public Father { 
public: virtual void show() { cout << "children  show" << endl; }
};int main() { Father *father = new Father(); father->show();  // 调用父类的show函数 Children *children = new Children();children->show();  // 调用子类的show函数Father *p = new Children();p->show();  // 调用哪个类中的show函数? 就看父类的指针,指向了哪个对象,指向的是子类,所以调用的就是子类的函数return 0;
}

在这里插入图片描述

总结: 多态需要以下三个条件
1、有继承关系
2、子类重写了父类的函数
3、定义一个父类的指针变量,指向子类调用子类,指向父类,调用父类。

8、 初始化列表的前序
  • 先说一下初始化列表作用和构造函数一样,构造函数是有默认的逻辑的,但很多时候,默认的逻辑不太像,需要我们指定,就用到了初始化列表。初始化列表,就用来明确指出,到底用哪个构造函数。

之前说构造函数是类生成一个对象之后,自动调用用来初始化的函数,如果一个类A里有一个 构造函数,类B继承了类A,那么当类B生成一个对象后,有几种情况。

1、类B 在初始化一个对象之后,会调用A里面这个构造函数么 ?

2、类B 里面 如果有一个自己的构造函数,类B的对象会调用哪个构造函数呢 ?
都会调用,且顺序是先调用父类的构造函数,即 A 里面的构造函数,再调用B里面的构造函数。

#include <iostream>using namespace std;class A {
public:A() {cout << this << "父类A的 无参数构造函数被调用\n";}
};class B : public A {
public:B() {cout << this << "父类B的 无参数构造函数被调用\n";}
};int main( ) {   B obj_1;   return 0;   
}

在这里插入图片描述

3、构造函数在类中,可以有多个,区别只是参数不同,那么我们默认的调用顺序是什么呢?

  • 首先还是先调用父类的构造函数,再调用子类的构造函数
  • 当父类有 无参构造函数 时,在自动调用子类构造函数之前,一定会自动调用父类的无参构造函数
  • 父类没有无参构造函数,那么子类必须指定父类调用哪一个,即使父类只有一个带参数的构造函数,否则编译会报错。这个和子类调用自己的构造函数不同,调用自己的还是和单个类生成对象一样, 即 :看传入的参数和哪个构造函数入参类型一样
//当父类有无参构造函数时,在自动调用子类构造函数之前,一定会自动调用父类的无参构造函数
#include <iostream> using namespace std; class A { 
public: A() { cout << this << "父类A的 无参数构造函数被调用\n"; }A(int temp) { cout << this << "父类A的 有参数构造函数被调用\n"; }
};class B : public A {
public:B() {cout << this << "父类B的 无参数构造函数被调用\n";}B(int temp) {cout << this << "父类B的 有参数构造函数被调用\n";}
};int main() {B obj_1;cout << "---------------\n";B obj_2(11);return 0;
}

在这里插入图片描述

//父类没有***无参构造函数***,那么子类**必须指定**父类调用哪一个,即使父类只有一个带参数的构造函数,否则编译会  报错 Constructor for 'B' must explicitly initialize the base class 'A' which does not have a default constructor “B”的构造函数必须显式初始化没有默认构造函数的基类“A”#include <iostream>using namespace std;class A {
public:
//    A() {
//        cout << this << "父类A的 无参数构造函数被调用\n";
//    }A(int temp) {cout << this << "父类A的 有参数构造函数被调用\n";}
};class B : public A {
public:B() {cout << this << "父类B的 无参数构造函数被调用\n";}B(int temp) {cout << this << "父类B的 有参数构造函数被调用\n";}
};int main(int argc, char **argv) {B obj_1;cout << "---------------\n";B obj_2(11);return 0;
}

在这里插入图片描述

4、C++为什么要设计成,子类对象生成的时候,父类A中的构造函数也会被调用呢?

简单点理解,因为,如果父类中如果有private修饰的成员变量的话,在子类中是无法被调用的,也就是意味着子类不能够对这些private成员变量进行初始化,因此就需要调用父类的构造函数

AI解释一下:
C++设计成在子类对象生成时,父类(或基类)A中的构造函数也会被调用的原因,主要基于面向对象编程(OOP)的继承机制和对象构造的逻辑。以下是对这一设计的详细解释:

  1. 对象构造的完整性
    在C++中,一个子类对象是由其基类部分和自身特有的部分组成的。为了确保子类对象在构造时能够完整地表示其应有的状态,基类部分必须被正确初始化。这包括基类成员变量的初始化和基类构造函数中可能执行的任何其他初始化操作。

  2. 资源管理和依赖关系
    基类构造函数通常负责初始化基类对象所需的资源,如内存分配、文件打开、网络连接等。如果基类构造函数不被调用,那么这些资源可能不会被正确分配或初始化,从而导致资源泄漏、访问违规或程序崩溃。此外,基类构造函数还可能设置基类对象与其他对象之间的依赖关系,这些关系在子类对象中也必须得到维护。

  3. 多态性和接口一致性
    C++支持多态性,允许通过基类指针或引用来操作派生类对象。为了确保这种操作的安全性,基类部分必须被正确初始化。如果基类构造函数不被调用,那么通过基类接口访问派生类对象时可能会遇到未定义的行为。此外,基类构造函数还可能提供了一组通用的初始化逻辑,这些逻辑在派生类中应该被保留和扩展。

  4. 构造顺序和初始化链
    C++规定了对象的构造顺序:首先调用基类的构造函数(如果有多个基类,则按照它们在派生类中的声明顺序依次调用),然后按照成员变量的声明顺序调用成员变量的构造函数(如果有的话),最后调用派生类的构造函数。这种顺序确保了对象在构造时的逻辑一致性和可预测性。

  5. 代码重用和减少冗余
    如果每个派生类都需要手动复制基类的初始化代码,那么这将导致大量的代码冗余和难以维护。通过自动调用基类构造函数,C++允许派生类重用基类的初始化逻辑,从而简化代码并提高可维护性。

综上所述,C++设计成在子类对象生成时自动调用父类构造函数,是为了确保对象构造的完整性、正确管理资源、维护多态性和接口一致性、保证构造顺序的逻辑一致性以及减少代码冗余和提高可维护性。这是面向对象编程中继承机制的一个基本特性,也是C++语言设计的一个重要方面。

总结:

  • 创建子类对象时,程序首先调用父类的构造函数,然后再调用子类的构造函数

    • 父类构造函数负责初始化继承的数据成员

    • 子类构造函数主要用于初始化新增的数据成员

  • 子类构造函数总是需要调用一个父类构造函数;当父类没有无参数的构造函数时,就必须显式指明调用哪一个构造函数

5、怎么显示的指明调用哪个构造函数呢,就需要用到 初始化列表 了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/461834.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Oasis 500M:开源的实时生成交互式视频内容的 AI 模型

❤️ 如果你也关注大模型与 AI 的发展现状&#xff0c;且对 AI 应用开发非常感兴趣&#xff0c;我会快速跟你分享最新的感兴趣的 AI 应用和热点信息&#xff0c;也会不定期分享自己的想法和开源实例&#xff0c;欢迎关注我哦&#xff01; &#x1f966; 微信公众号&#xff5c…

微服务实战系列之玩转Docker(十六)

导览 前言Q&#xff1a;基于容器云如何实现高可用的配置中心一、etcd入门1. 简介2. 特点 二、etcd实践1. 安装etcd镜像2. 创建etcd集群2.1 etcd-node12.2 etcd-node22.3 etcd-node3 3. 启动etcd集群 结语系列回顾 前言 Docker&#xff0c;一个宠儿&#xff0c;一个云原生领域的…

固定翼无人机飞行操控技术详解

固定翼无人机飞行操控技术是一个复杂而精密的领域&#xff0c;涵盖了从起飞准备到实际飞行操作&#xff0c;再到安全降落的各个环节。以下是对固定翼无人机飞行操控技术的详细解析&#xff1a; 一、起飞准备 1. 设备检查&#xff1a; 确保无人机充满电&#xff0c;检查电池状…

文件描述符fd 和 缓冲区

目录 1.文件描述符 fd 1.1文件打开的返回值fd&#xff08;重点&#xff09; 1.2.如何理解Linux下的一切皆文件 1.3.文件fd的分配原则 && 输出重定向 1.4.dup2()函数 2.缓冲区 2.1. 概念 2.2. 存在的原因 2.3. 类型(刷新方案) 2.4. 存放的位置 1.文件描述符 fd …

【qt qtcreator使用】【正点原子】嵌入式Qt5 C++开发视频

QT creator 的使用 一.qtcreator的介绍  (1).ui界面介绍    [1].软件左侧界面部分    [2].软件界面下方部分    [3].UI设计界面 (2).debug的使用 (3).项目的配置 (4).帮助文档的使用 (5).构建多个项目 二.qtcreator 的设置 (1).qt编译套件的设置 (2).设置快…

Vue3和Springboot前后端简单部署

一、Vue3Springboot 的前后端简单部署 (在win下面部署) 1、前端实现部署 思想: 前端打包项目后、放到nginx中进行部署 1、nginx 安装 和 解压 1、下载 nginx.zip win版本 解压就可以 2、解压后、启动程序 3、访问 nginx 欢迎页面 http://localhost/ 80 端口 可以省略 直接访…

【大数据学习 | kafka】kafka的ack和一致性

1. ack级别 上文中我们提到过kafka是存在确认应答机制的&#xff0c;也就是数据在发送到kafka的时候&#xff0c;kafka会回复一个确认信息&#xff0c;这个确认信息是存在等级的。 ack0 这个等级是最低的&#xff0c;这个级别中数据sender线程复制完毕数据默认kafka已经接收到…

【分布式技术】分布式事务深入理解

文章目录 概述产生原因关键点 分布式事务解决方案3PC3PC的三个阶段&#xff1a;3PC相比于2PC的改进&#xff1a;3PC的缺点&#xff1a; TCCTCC事务的三个阶段&#xff1a;TCC事务的设计原则&#xff1a;TCC事务的适用场景&#xff1a;TCC事务的优缺点&#xff1a;如何解决TCC模…

Linux高阶——1027—

1、守护进程的基本流程 1、父进程创建子进程&#xff0c;父进程退出 守护进程是孤儿进程&#xff0c;但是是工程师人为创建的孤儿进程&#xff0c;低开销模式运行&#xff0c;对系统没有压力 2、子进程&#xff08;守护进程&#xff09;脱离控制终端&#xff0c;创建新会话 …

centos7配置keepalive+lvs

拓扑图 用户访问www.abc.com解析到10.4.7.8&#xff0c;防火墙做DNAT将访问10.4.7.8:80的请求转换到VIP 172.16.10.7:80&#xff0c;负载均衡器再将请求转发到后端web服务器。 实验环境 VIP&#xff1a;负载均衡服务器的虚拟ip地址 LB &#xff1a;负载均衡服务器 realserv…

服务器宝塔安装哪吒监控

哪吒文档地址&#xff1a;https://nezha.wiki/guide/dashboard.html 一、准备工作 OAuth : 我使用的gitee&#xff0c;github偶尔无法访问&#xff0c;不是很方便。第一次用了极狐GitLab&#xff0c;没注意&#xff0c;结果是使用90天&#xff0c;90天后gg了&#xff0c;无法登…

【动手学强化学习】part6-策略梯度算法

阐述、总结【动手学强化学习】章节内容的学习情况&#xff0c;复现并理解代码。 文章目录 一、算法背景1.1 算法目标1.2 存在问题1.3 解决方法 二、REINFORCE算法2.1 必要说明softmax()函数交叉熵策略更新思想 2.2 伪代码算法流程简述 2.3 算法代码2.4 运行结果2.5 算法流程说明…

单片机内存管理和启动文件

一、常见存储器介绍 FLASH又称为闪存&#xff0c;不仅具备电子可擦除可编程(EEPROM)的性能&#xff0c;还不会断电丢失数据同时可以快速读取数据&#xff0c;U盘和MP3里用的就是这种存储器。在以前的嵌入式芯片中&#xff0c;存储设备一直使用ROM(EPROM)&#xff0c;随着技术的…

Python画图3个小案例之“一起看流星雨”、“爱心跳动”、“烟花绚丽”

源码如下&#xff1a; import turtle # 导入turtle库&#xff0c;用于图形绘制 import random # 导入random库&#xff0c;生成随机数 import math # 导入math库&#xff0c;进行数学计算turtle.setup(1.0, 1.0) # 设置窗口大小为屏幕大小 turtle.title("流星雨动画&…

SQL-lab靶场less1-4

说明&#xff1a;部分内容来源于网络&#xff0c;如有侵权联系删除 前情提要&#xff1a;搭建sql-lab本地靶场的时候发现一些致命的报错&#xff1a; 这个程序只能在php 5.x上运行&#xff0c;在php 7及更高版本上&#xff0c;函数“mysql_query”和一些相关函数被删除&#xf…

AutoGLM:智谱AI的创新,让手机成为你的生活全能助手

目录 引言一、AutoGLM&#xff1a;开启AI的Phone Use时代二、技术核心&#xff1a;AI从“语言理解”到“执行操作”三、实际应用案例&#xff1a;AutoGLM的智能力量1. 智能生活管理&#x1f34e;2. 社交网络的智能互动&#x1f351;3. 办公自动化&#x1f352;4. 电子商务的购物…

ceph补充介绍

SDS-ceph ceph介绍 crushmap 1、crush算法通过计算数据存储位置来确定如何存储和检索&#xff0c;授权客户端直接连接osd 2、对象通过算法被切分成数据片&#xff0c;分布在不同的osd上 3、提供很多种的bucket&#xff0c;最小的节点是osd # 结构 osd (or device) host #主…

Scrapy源码解析:DownloadHandlers设计与解析

1、源码解析 代码路径&#xff1a;scrapy/core/downloader/__init__.py 详细代码解析&#xff0c;请看代码注释 """Download handlers for different schemes"""import logging from typing import TYPE_CHECKING, Any, Callable, Dict, Gener…

如何解决docker镜像下载失败问题

经常用docker的朋友都知道&#xff0c;docker hub的镜像仓库经常访问不通 rootiZwz97kfjnf78copv1ae65Z:~# docker pull ubuntu:18.04 Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.…

探索 ONLYOFFICE:开源办公套件的魅力

文章目录 引言一、ONLYOFFICE 产品介绍与历史1.1 ONLUOFFICE 介绍1.2 ONLYOFFICE发展历史 二、ONLYOFFICE 的核心功能2.1 文档处理2.2 演示文稿 三、ONLYOFFICE 部署与安装四、ONLYOFFICE 产品优势和挑战五、ONLYOFFICE 案例分析六、ONLYOFFICE 的未来发展七、全文总结 引言 在…