C++总结

目录

一、面向对象的三大特性

二、引用

2.1 概念

2.2特性

三、类与对象

3.1概念

3.2 类的内容

3.3对象的创建

四、构造函数与析构函数

五、封装

六、继承

6.1概念与基础使用

6.2 继承权限

6.2.1 权限修饰符

6.2.2 继承权限

6.3构造函数

6.3.1 派生类与基类的构造函数关系

6.3.2 解决方法

6.3.2.1 补充基类的无参构造函数

6.3.2.2手动在派生类中调用基类构造函数

1)透传构造

2)委托构造

3)继承构造

6.4 多重继承

6.4.1概念

6.4.2可能出现的问题

6.4.2.1 问题1-重名问题

6.4.2.2 问题2-菱形继承

七、多态

7.1 什么是多态?

7.2 多态的概念

7.3 函数覆盖

7.4 虚函数的定义

7.5 多态实现

7.6 多态的原理

7.7 虚析构函数


一、面向对象的三大特性

封装->继承->多态

二、引用

2.1 概念

引用就是给某个变量或常量起别名,对引用进行操作与操作原变量或常量完全相同。

int main()
{
    int a  =  10086;
    int &b = a ; //  b = 10086
    b ++ ; // b = 10087
}

2.2特性

1)可以改变引用的值,但不能再次成为其他变量的引用

int main()
{
    string str1 = "Gagahao";
    string & str2 = str1; // string & str3 = str2;  错误 str2已经是str1的引用了,不能被str3再次引用
}

2)声明引用时必须初始化

int main()
{//int &a ;
}

  1. 声明引用时不能初始化为NULL

int main()
{//int &a = NULL;
}

4)声明引用的时候,初始化的值可以是纯数值,但是此时要使用const关键字修饰引用,表示改引用为常量引用。这样的引用的值不可变。

int main()
{const int &num = 666 //常量引用//num = 123; //错误 常量引用的数值不可变  
}

5)可以对指针建立引用

int main()
{int a = 1;int &b = a;int *c = &b;    // C同时指向了A和B
}

6)可以使用const修饰引用,此时如果原变量的值改变,引用的值也会发生改变

int main()
{int a = 2;const int &b = a;    a++    cout << a << " " << &a << endl; // 3 0x61fe88
    cout << b << " " << &b << endl; // 3 0x61fe88
}

三、类与对象

3.1概念

类:类是一个抽象的概念,用于描述同一类对象的特点。

对象:根据类的概念所创造的实体。

3.2 类的内容

类中最基础的内容包括两部分,一个是属性(成员变量),一个是行为(成员函数)。

成员 = 成员函数+成员变量。

3.3对象的创建

C++中存在两种类型的对象:

栈内存对象:对象所在的{}执行完成后,自动被销毁。调用栈内存对象时用"."

堆内存对象:必须使用关键字new创建,使用关键字delete销毁。如果没有及时销毁的话,会造成内存泄漏,调用堆内存对象时用"->".

四、构造函数与析构函数

构造函数

析构函数

函数名称为类名

函数名称为~类名

功能为创建对象并初始化

功能为在对象销毁时回收资源

调用事件:在创建对象时调用

在对象销毁时自动被调用

有参数,支持重载和默认值

无参数,不支持重载和默认值

class Student
{
private:string name;
public:Student(string name):name(name){}~Student(){}
}
int main()
{Student *s1 = new Student("赵华润");......//内部执行代码delete s1;return 0;
}

注意:如果我们不手动编写构造函数时,编译器会自动添加一个无参且函数体为空的构造函数。相同,如果不手写析构函数,编译器也会为我们添加上述代码中的析构函数。

五、封装

概念:封装是将类内的某些信息隐藏(私有化),并提供一些外部访问接口,可通过外部访问接口来访问这些信息。

class MobilePhone
{
private: 
    string brand;
public:
    string get_brand() // getter:读函数
    {
        return brand;
    }    void set_brand(string b) // setter:写函数
    {
        brand = b;
    }
} int main()
{
    MobilePhone mp1;
    mp1.set_brand("钛合金8848"); cout << mp1.get_brand() << endl;  //钛合金8848return 0
}

六、继承

6.1概念与基础使用

继承就是在一个已经存在的类的基础上新建立一个类,新创建的类拥有之前类的特性。体现了代码复用的思想。

  • 已经存在的类被称为“基类 Base Class”或“父类”
  • 新创建的类被称为“派生类”或“子类Sub Class”

class Father
{
publicvoid func(){
        cout << "叭叭叭" << endl;}
}class Sonpublic Father
{
public
}int main()
{
    Son *s1 = new Son;
    son->func(); //"叭叭叭"delete s1;return 0;
}

6.2 继承权限

6.2.1 权限修饰符

类内

派生类中

全局

private

×

×

protected

×

public

6.2.2 继承权限

1)公有继承

派生类从基类中继承的成员的权限不发生改变。

2)保护继承

在保护继承中,基类的所有成员均可以被派生类继承。但是基类的私有成员无法被派生类直接访问,基类的保护成员和公有成员继承到派生类中作为派生类的保护成员。

3)私有继承

在私有继承中,基类的所有成员均可以被派生类继承,但是基类的私有成员无法被派生类直接访问,基类的保护成员和公有成员继承到派生类中作为派生类的私有成员。

6.3构造函数

构造函数和析构函数不能被继承

6.3.1 派生类与基类的构造函数关系

// 基类
class Father
{
private:
    string name = "孙";
public:// 有参构造函数Father(string name):name(name){}    string get_name(){return name;}
};// 派生类
class Son:public Father
{
public
};int main()
{
//    Son s; // 找不到基类的无参构造函数
//    Son s("张"); // 没有匹配的构造函数return 0;
}

6.3.2 解决方法

6.3.2.1 补充基类的无参构造函数

class Father
{
private:
    string name = "孙";
public:// 补充基类无参构造函数Father(){
        cout << "构造函数被调用了" << endl;}// 有参构造函数Father(string name):name(name){}    string get_name(){return name;}
};// 派生类
class Son:public Father
{
public:// 编译器自动添加的构造函数Son():Father(){
        cout << "派生类构造函数被调用了" << endl;}
};int main()
{
    Son s; return 0;
}

6.3.2.2手动在派生类中调用基类构造函数
1)透传构造

class Father
{
private:
    string name = "孙";
public:// 有参构造函数Father(string name):name(name){}
    string get_name(){return name;}
};class Son:public Father
{
public:// 透传构造Son():Father("张"){}// 派生类有参构造函数,调用基类有参构造函数Son(string fn):Father(fn){}
};int main()
{
    Son s;
    cout << s.get_name() << endl; //张    Son s1("王");
    cout << s1.get_name() << endl;//王return 0;
}

2)委托构造

一个类的构造函数可以调用这个类的另一个构造函数,但是要避免循环委托。

class Father
{
private:
    string name = "孙";
public:
    // 有参构造函数
    Father(string name):name(name){}
    string get_name()
    {
        return name;
    }
};// 派生类
class Son:public Father
{
public:
    // 委托构造
    Son():Son("张"){}
    // 派生类有参构造函数,调用基类有参构造函数
    Son(string fn):Father(fn){}
};int main()
{
    Son s;
    cout << s.get_name() << endl; //
    return 0;
}

3)继承构造

只需要一句话,就可以自动给派生类添加n(n为基类构造函数的个数(不包含默认无参构造函数))个构造函数。并且每个派生类的构造函数的格式都与基类相同,每个派生类的构造函数都通过透传构造调用对应格式的基类构造函数。

class Father
{
private:
    string name = "孙";
public:
    Father():Father("张"){}
    // 有参构造函数
    Father(string name):name(name){}
    string get_name()
    {
        return name;
    }
};
// 派生类
class Son:public Father
{
public:
    using Father::Father;
};int main()
{
    Son s;
    cout << s.get_name() << endl;
    return 0;
}

6.4 多重继承

6.4.1概念

一个派生类可以有多个基类。派生类对于每个基类的关系仍然可以看作是一个单继承。

class Sofa
{
public:void sit(){
        cout << "沙发可以坐着" << endl;}
};class Bed
{
public:void lay(){
        cout << "床可以躺着" << endl;}
};// 多重继承
class SofaBed :public Sofa,public Bed
{
public:
};int main()
{
    SofaBed sb;
    sb.lay(); //床可以躺着
    sb.sit(); //沙发可以坐着return 0;
}

6.4.2可能出现的问题

6.4.2.1 问题1-重名问题

当多个基类具有重名成员时,编译器在编译的过程中会出现二义性的问题。

解决方式:使用基类的类名::方式调用。

class Sofa
{
public:
    void clean()
    {
        cout << "打扫沙发" << endl;
    }
};class Bed
{
public:
    void clean()
    {
        cout << "打扫床" << endl;
    }
};// 多重继承
class SofaBed :public Sofa,public Bed
{
public:
};int main()
{
    SofaBed sb;
    sb.Sofa::clean();
    sb.Bed::clean();
    return 0;
}

6.4.2.2 问题2-菱形继承

当一个基类有多个派生类,且这些派生类又有一个共同基类时。就会出现二义性问题,这种继承方式称为菱形(钻石)继承。

有两种解决方式:

  1. 使用基类的类名::方式调用

//跟上边代码差不多,自己补充

  1. 使用虚继承

当出现虚继承时,Furniture类会生成一张虚基类表。这个表并不会占用任何对象的存储空间,属于Furniture类持有,在程序启动时加载进内存,表中记录了Furniture函数的调用地址偏移量。

Bed和Sofa对象会出现一个隐藏的成员变量指针,指向Furniture类中的虚基类表,占用对象四个字节。

SofaBed继承Sofa和Bed时,SofaBed类对象会同时拥有两个虚基类表指针成员,在调用时通过查表解决二义性。

class Furniture
{
public:
    void func()
    {
        cout << "家具厂里有家具" << endl;
    }
};class Sofa:virtual public Furniture
{
public:
};class Bed:virtual public Furniture
{
public:
};// 多重继承
class SofaBed :public Sofa,public Bed
{
public:
};int main()
{
    SofaBed sb;
    sb.func(); //家具厂里有家具
    return 0;
}

七、多态

7.1 什么是多态?

在面向对象编程中,我们通常将多态分为两种类型:静态多态(静态多态,也被称为编译时多态)和动态多态(动态多态,也被称为运行时多态)。这两种多态性都是多态概念的不同表现方式。

静态多态

  • 静态多态是指在编译时就能确定要调用的方法,通过函数重载和运算符重载来实现。

动态多态

  • 动态多态是指在运行时根据对象的实际类型来确定要调用的函数,通过继承和函数覆盖来实现。

静态多态发生在编译时,因为在编译阶段编译器就可以确定要调用的函数。

动态多态发生在运行时,因为具体调用那个函数是在程序运行时根据实际对象的实际类型来确定的。

7.2 多态的概念

多态可以理解为"一种接口,多种状态"只需要编写一个函数接口,根据传入的参数类型,执行不同的策略代码。

多态的使用有三个前提条件:

  • 公有继承
  • 函数覆盖
  • 基类的引用/指针指向派生类对象

多态的优点:多态的优势包括代码的灵活性、可扩展性和可维护性更好。它能使代码更具有通用性,减少重复代码的编写,并且能够轻松的添加新的派生类或拓展现有的功能。

多态的缺点:缺点包括代码的复杂性,运行效率、不易读。当类的继承关系复杂使,理解和维护多态的代码会变得困难。多态在运行时会产生一些额外的开销。

7.3 函数覆盖

函数覆盖和函数隐藏比较相似,但是函数隐藏不支持多态,而函数覆盖是多态的前提条件。函数覆盖比函数隐藏有一下几点区别:

  • 函数隐藏是派生类中存在与基类中同名同参的函数,编译器会将基类的同名同参数的函数进行隐藏。
  • 函数覆盖是基类中定义了一个虚函数,派生类编写一个同名同参数的函数将基类中的虚函数进行重写并覆盖。注意:覆盖的基类函数必须是虚函数。

7.4 虚函数的定义

一个函数使用virtual关键字修饰,就是虚函数。虚函数是函数覆盖的前提。在Qt Creator中虚函数的函数名称使用斜体字。

class Animal
{
public:// 虚函数virtual void eat(){
        cout << "动物爱吃饭" << endl;}
};

虚函数具有以下性质:

  • 虚函数具有传递性,基类中的覆盖的函数是虚函数,派生类中新覆盖的函数也是虚函数。

class Animal
{
public:virtual void eat(){
        cout << "动物爱吃饭" << endl;}
};class Dog :public Animal
{// 覆盖基类中的虚函数,派生类的virtual关键字可写可不写void eat(){
        cout << "狗爱吃骨头" << endl;}
};

  • 只有普通成员函数与析构函数可以声明为虚函数,但构造函数和静态成员函数不能声明为虚函数。

class Animal
{
public:
//  错误 构造函数不能声明为虚函数
//    virtual Animal(){}  // 错误 静态函数不能为虚函数
//    virtual static void testStatic()
};

  • 在C++11中,可以在派生类的新覆盖的函数上使用override关键字验证覆盖是否成功。

#include <iostream>
using namespace std;class Animal
{
public:virtual void eat(){
        cout << "动物爱吃饭" << endl;}void funHide(){
        cout << "测试:override关键字" << endl;}};class Dog :public Animal
{// 覆盖基类中的虚函数,派生类的virtual关键字可写可不写void eat()override{
        cout << "狗爱吃骨头" << endl;}
//    void funHide()override //错误 funHide不是虚函数
//    {
//        cout << "测试:override关键字" << endl;
//    }
};

7.5 多态实现

实现动态多态,需要有三个前提条件。

  • 公有继承(已经实现)
  • 函数覆盖(已经实现)
  • 基类的指针/引用指向派生类对象

【思考】为什么要基类的指针指向派生类的对象呢?

  • 实现多态:当使用基类的指针或引用指向派生类对象时,程序在运行时会根据对象的实际类型来调用相应的函数,而不是根据指针或引用的类型。
  • 统一接口:基类的指针可以作为一个通用的接口,用于操作不同类型的派生类对象。这样可以让代码更灵活,减少重复代码的编写。

class Animal
{
public:// 虚函数virtual void eat(){
        cout << "动物爱吃饭" << endl;}
};class Dog :public Animal
{
public:void eat()override{
        cout << "狗爱吃骨头" << endl;}
};int main()
{
    Animal *a1 = new Dog;
    a1->eat();  // 狗爱吃骨头return 0;
}

我们也可以提供接口,参数设计为基类的指针或者是引用,这样这个函数就可以访问到此基类所有派生类的虚函数了。

#include <iostream>
using namespace std;class Animal
{
public:// 虚函数virtual void eat(){
        cout << "动物爱吃饭" << endl;}
};class Dog :public Animal
{
public:void eat()override{
        cout << "狗爱吃骨头" << endl;}
};// 提供通用函数,形参为基类指针
void animal_eat1(Animal *a1)
{// 100 代码// 需要用到派生类的虚函数参与逻辑处理
    a1->eat();
}// 提供通用函数,形参为基类引用
void animal_eat2(Animal &a1)
{
    a1.eat();
}int main()
{
    Dog d1;
    Dog * d2 = new Dog;animal_eat2(d1);    // 狗爱吃骨头animal_eat1(d2);  // 狗爱吃骨头return 0;
}

7.6 多态的原理

具有虚函数的类会存在一张虚函数表,每个类的对象内部都会有一个隐藏的虚函数表指针成员,指向当前类的虚函数表。

多态实现流程

在代码运行时,通过对象的虚函数表指针找到虚函数表,在表中定位到虚函数的调用地址,从而执行对应的虚函数的内容。

7.7 虚析构函数

如果不使用虚析构函数,且基类指针或引用指向派生类对象,使用delete销毁对象时,只能触发基类的析构函数,如果在派生类中申请内存等资源,则会导致无法释放,出现内存泄漏的问题。

解决方案给基类的析构函数使用virtual修饰为虚析构函数,通过传递性可以把各个派生类的析构函数都变为虚析构函数,因此建议给一个可能为基类类中的析构函数设置成虚析构函数。

class Animal
{
public:// 虚析构函数virtual ~Animal(){
        cout << "析构函数Animal" << endl;}
};class Dog :public Animal
{
public:~Dog(){
        cout << "析构函数Dog" << endl;}
};int main()
{
    Animal *a1 = new Dog;delete a1;return 0;
}

  • QT补充
    1. QT课程定位

1. Qt的学习需要C++基础。

2. Qt是C++的实践课,C++的很多抽象理论可以在开发案例中体现。

3. Qt的API非常多且代码量大,千万不要死记硬背。

    1. QT可能用到的知识点(可能)
  1. this指针结合多态
  2. 容器(Map键值对、list列表)
  3. 数据库
  4. IO进程(多进程、多线程)
  5. 网络编程(TCP/UDP)
  6. 抽象类
    1. 提前预习的知识点

槽函数!!!

(得(de)槽函数者得(de)QT)

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

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

相关文章

2024 CSS保姆级教程二 - BFC详解

前言 - CSS中的文档流 在介绍BFC之前&#xff0c;需要先给大家介绍一下文档流。​ 我们常说的文档流其实分为定位流、浮动流、普通流三种。​ ​ 1. 绝对定位(Absolute positioning)​ 如果元素的属性 position 为 absolute 或 fixed&#xff0c;它就是一个绝对定位元素。​ 在…

在PHP8内,用Jenssegers MongoDB扩展来实现Laravel与MongoDB的集成

在现代 web 开发中&#xff0c;MongoDB 作为一种流行的 NoSQL 数据库&#xff0c;因其灵活的文档结构和高性能而受到许多开发者的青睐。Laravel&#xff0c;作为一个优雅的 PHP Web 框架&#xff0c;提供了丰富的功能和优雅的代码风格。本文将指导你如何在 Laravel 项目中集成 …

GPU 环境搭建指南:如何在裸机、Docker、K8s 等环境中使用 GPU

本文主要分享在不同环境&#xff0c;例如裸机、Docker 和 Kubernetes 等环境中如何使用 GPU。 跳转阅读原文&#xff1a;GPU 环境搭建指南&#xff1a;如何在裸机、Docker、K8s 等环境中使用 GPU 1. 概述 仅以比较常见的 NVIDIA GPU 举例&#xff0c;系统为 Linux&#xff0c;…

Axure设计之左右滚动组件教程(动态面板)

很多项目产品设计经常会遇到左右滚动的导航、图片展示、内容区域等&#xff0c;接下来我们用Axure来实现一下左右滚动的菜单导航。通过案例我们可以举一反三进行其他方式的滚动组件设计&#xff0c;如常见的上下滚动、翻页滚动等等。 一、效果展示&#xff1a; 1、点击“向左箭…

每天五分钟深度学习框架pytorch:如何加载手写字体数据集mnist?

本文重点 那个这节课程之后,我们就将通过代码的方式来搭建CNN和RNN模型,然后训练,我们使用的数据集为pytorch中已经封装好的数据集,比如mnist,cafir10,本文我们学习一下如何在pytorch中使用它们,然后为之后的章节做准备,现在我们拿mnist来举例。 mnist和cafir10 MIN…

itextpdf打印A5的问题

使用A5打印的时候&#xff0c;再生成pdf是没有问题的。下面做了一个测试&#xff0c;在打印机中&#xff0c;使用A5的纸张横向放入&#xff0c;因为是家用打印机&#xff0c;A5与A4是同一个口&#xff0c;因此只能这么放。 使用itextpdf生成pdf&#xff0c;在浏览器中预览pdf是…

AJAX 全面教程:从基础到高级

AJAX 全面教程&#xff1a;从基础到高级 目录 什么是 AJAXAJAX 的工作原理AJAX 的主要对象AJAX 的基本用法AJAX 与 JSONAJAX 的高级用法AJAX 的错误处理AJAX 的性能优化AJAX 的安全性AJAX 的应用场景总结与展望 什么是 AJAX AJAX&#xff08;Asynchronous JavaScript and XML…

CKA认证 | Day1 k8s核心概念与集群搭建

第一章 Kubernetes 核心概念 1、主流的容器集群管理系统 容器编排系统&#xff1a; KubernetesSwarmMesos Marathon 2、Kubernetes介绍 Kubernetes是Google在2014年开源的一个容器集群管理系统&#xff0c;Kubernetes简称K8s。 Kubernetes用于容器化应用程序的部署&#x…

web实操1——只使用tomcat发布网站

安装tomcat 下载 肯定是去官网&#xff1a; http://tomcat.apache.org/ 下载之后&#xff0c;解压&#xff1a; &#xff01;&#xff01;解压后&#xff1a; logs日志&#xff1a;就是一些输出&#xff0c;输到文本里。 temp:一些临时文件(不用管) webapps:放网站的 work&…

数据结构:七种排序及总结

文章目录 排序一插入排序1直接插入排序2希尔排序二选择排序3直接选择排序4堆排序三 交换排序5冒泡排序6快速排序四 归并排序7归并排序源码 排序 我们数据结构常见的排序有四大种&#xff0c;四大种又分为七小种&#xff0c;如图所示 排序&#xff1a;所谓排序&#xff0c;就是…

A day a tweet(sixteen)——The better way of search of ChatGPT

Introducing ChatGPT search a/ad.及时的/及时地 ChatGPT can now search the web in a much better way than before so you get fast, timely a.有关的(relative n.亲戚,亲属;同类事物 a.比较的&#xff1b;相对的) answers with link…

HTMLCSS:呈现的3D树之美

效果演示 这段代码通过HTML和CSS创建了一个具有3D效果的树的图形&#xff0c;包括分支、树干和阴影&#xff0c;通过自定义属性和复杂的变换实现了较为逼真的立体效果。 HTML <div class"container"><div class"tree"><div class"…

系统规划与管理师——第十二章 职业素养与法侓法规

目录 职业素养 职业道德 行为规范 职业责任 对客户和公众的责任 法律法规 法律概念 法律体系 诉讼时效 民事诉讼时效 刑事追诉时效 常用的法律法规 合同法 招投标法 著作权法 政府采购法 劳动法 知识产权法 刑法修正案&#xff08;七) IT服务的广泛应用不仅…

HAL库硬件IIC驱动气压传感器BMP180

环境 1、keilMDK 5.38 2、STM32CUBEMX 初始配置 默认即可。 程序 1、头文件 #ifndef __BMP_180_H #define __BMP_180_H#include "main.h"typedef struct {float fTemp; /*温度&#xff0c;摄氏度*/float fPressure; /*压力&#xff0c;pa*/float fAltitude; /*…

沈阳乐晟睿浩科技有限公司抖音小店展望未来

在当今数字化浪潮汹涌的时代&#xff0c;电子商务以其独特的魅力和无限潜力&#xff0c;正深刻改变着人们的消费习惯与商业模式。抖音小店作为短视频与电商深度融合的产物&#xff0c;凭借其庞大的用户基础、精准的内容推送机制以及独特的购物体验&#xff0c;迅速崛起为电商领…

【论文复现】自动化细胞核分割与特征分析

本文所涉及所有资源均在这里可获取。 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 论文复现 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 自动化细胞核分割与特征分析 引言效果展示HoverNet概述HoverNet原理分析整…

ESP32 gptimer通用定时器初始化报错:assert failed: timer_ll_set_clock_prescale

背景&#xff1a;IDF版本V5.1.2 &#xff0c;配置ESP32 通用定时器&#xff0c;实现100HZ&#xff0c;占空比50% 的PWM波形。 根据乐鑫官方的IDF指导文档设置内部计数器的分辨率&#xff0c;计数器每滴答一次相当于 1 / resolution_hz 秒。 &#xff08;ESP-IDF编程指导文档&a…

Jest项目实战(4):将工具库顺利迁移到GitHub的完整指南

开源代码&#xff1a;将工具库迁移到GitHub 随着项目的成熟和完善&#xff0c;将其开源不仅可以获得更多的用户和贡献者&#xff0c;还能促进项目本身的发展。GitHub作为全球最大的开源协作平台&#xff0c;自然成为了大多数开发者的首选。本文将引导您完成将工具库迁移至GitH…

C#线程池

目录 前言 线程 线程池 线程池的工作原理 重要方法 C#线程池总结 前言 线程池是一种多线程处理形式&#xff0c;它允许开发者将任务添加到队列中&#xff0c;然后线程池会自动管理线程的创建、分配和回收&#xff0c;以执行这些任务。线程池中的线程都是后台线程&#xf…

SpringBoot项目集成ONLYOFFICE

ONLYOFFICE 文档8.2版本已发布&#xff1a;PDF 协作编辑、改进界面、性能优化、表格中的 RTL 支持等更新 文章目录 前言ONLYOFFICE 产品简介功能与特点Spring Boot 项目中集成 OnlyOffice1. 环境准备2. 部署OnlyOffice Document Server3. 配置Spring Boot项目4. 实现文档编辑功…