C++11新特性【下】

一、lambda表達式

在C++98中,如果想要對一個數據集合中的元素進行排序,可以使用std::sort方法。如果待排序元素為自定義類型,需要用戶定義排序時的比較規則,隨著C++語法的發展,人們開始覺得上面的寫法太復雜了,每次為了實現一個algorithm算法, 都要重新去寫一個類,如果每次比較的邏輯不一樣,還要去實現多個類,特別是相同類的命名, 這些都給編程者帶來了極大的不便。因此,在C++11語法中出現了Lambda表達式。

1.1 lambda表達式語法:

lambda表達式書寫格式:[capture-list] (parameters) mutable -> return-type { statement }

lambda表達式各部分說明
  • [capture-list] : 捕捉列表,該列表總是出現在lambda函數的開始位置,編譯器根據[ ]來 判斷接下來的代碼是否為lambda函數,捕捉列表能夠捕捉上下文中的變量供lambda 函數使用。
  • (parameters):參數列表。與普通函數的參數列表一致,如果不需要參數傳遞,則可以連同()一起省略
  • mutable:默認情況下,lambda函數總是一個const函數,mutable可以取消其常量性。使用該修飾符時,參數列表不可省略(即使參數為空)。
  • ->returntype:返回值類型。用追蹤返回類型形式聲明函數的返回值類型,沒有返回值時此部分可省略。返回值類型明確情況下,也可省略,由編譯器對返回類型進行推導。
  • {statement}:函數體。在該函數體內,除了可以使用其參數外,還可以使用所有捕獲 到的變量。

注意:

在lambda函數定義中,參數列表和返回值類型都是可選部分,而捕捉列表和函數體可以為空。因此C++11中最簡單的lambda函數為:[]{}; 該lambda函數不能做任何事情。

int main()
{//lambda實現兩數相加auto add = [](int a, int b) {return a + b; };cout << add(1, 2) << endl;//lambda實現swapauto swap = [](int& a, int& b){int temp = a;a = b;b = temp;};
}
捕獲列表說明:

捕捉列表描述了上下文中那些數據可以被lambda使用,以及使用的方式傳值還是傳引用。

  • [var]:表示值傳遞方式捕捉變量var
  • [=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)
  • [&var]:表示引用傳遞捕捉變量var
  • [&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)
  • [this]:表示值傳遞方式捕捉當前的this指針

捕捉列表還可以混合捕捉:

	int a = 1;int b = 2;int c = 3;int d = 4;//除了a b傳值捕捉外,其他的變量都是引用捕捉auto func = [&, a, b]() mutable{a++;b++;c++;d++;};func();cout << a << " " << b <<" " << c << " " << d << " " << endl;// 1 2 4 5

注意:

  • 父作用域指包含lambda函數的語句塊
  • 捕捉列表不允許變量重復傳遞,否則就會導致編譯錯誤。比如:[=, a]:=已經以值傳遞方式捕捉了所有變量,捕捉a重復
  • 在塊作用域以外的lambda函數捕捉列表必須為空。
  • 在塊作用域中的lambda函數僅能捕捉父作用域中局部變量,捕捉任何非此作用域或者非局部變量都會導致編譯報錯。
  • lambda表達式之間不能相互賦值,即使看起來類型相同

ps:lambda表達式的類型不是直接由語言定義的具名類型,而是編譯器為每一個lambda表達式生成的唯一的、匿名的非聯合類類型(non-union class type)。這個類型沒有名字,因此你不能用它來直接聲明變量或作為函數的參數類型。但是,你可以通過auto關鍵字或者模板來間接地引用這個類型。即使兩個表達式的內容完全相同,其類型都是不一樣的

1.2?函數對象與lambda表達式

函數對象,又稱為仿函數,即可以想函數一樣使用的對象,就是在類中重載了operator()運算符的 類對象。

class Rate
{
public:Rate(double rate) : _rate(rate){}double operator()(double money, int year){return money * _rate * year;}
private:double _rate;
};
int main()
{// 函數對象double rate = 0.49;Rate r1(rate);r1(10000, 2);// lamberauto r2 = [=](double monty, int year)->double {return monty * rate * year;};r2(10000, 2);return 0;
}

從使用方式上來看,函數對象與lambda表達式完全一樣。 函數對象將rate作為其成員變量,在定義對象時給出初始值即可,lambda表達式通過捕獲列表可 以直接將該變量捕獲到。

實際在底層編譯器對于lambda表達式的處理方式,完全就是按照函數對象的方式處理的,即:如 果定義了一個lambda表達式,編譯器會自動生成一個類,在該類中重載了operator()。

二、新的類功能

默認成員函數

原來C++類中,有6個默認成員函數:

  1. 構造函數
  2. 析構函數
  3. 拷貝構造函數
  4. 拷貝賦值重載
  5. 取地址重載
  6. const 取地址重載

最后重要的是前4個,后兩個用處不大。默認成員函數就是我們不寫編譯器會生成一個默認的。 C++11 新增了兩個:移動構造函數和移動賦值運算符重載。

針對移動構造函數和移動賦值運算符重載有一些需要注意的點如下:

  • 如果你沒有自己實現移動構造函數,且沒有實現析構函數 、拷貝構造、拷貝賦值重載中的任 意一個。那么編譯器會自動生成一個默認移動構造。默認生成的移動構造函數,對于內置類 型成員會執行逐成員按字節拷貝,自定義類型成員,則需要看這個成員是否實現移動構造, 如果實現了就調用移動構造,沒有實現就調用拷貝構造。
  • 如果你沒有自己實現移動賦值重載函數,且沒有實現析構函數 、拷貝構造、拷貝賦值重載中 的任意一個,那么編譯器會自動生成一個默認移動賦值。默認生成的移動構造函數,對于內置類型成員會執行逐成員按字節拷貝,自定義類型成員,則需要看這個成員是否實現移動賦 值,如果實現了就調用移動賦值,沒有實現就調用拷貝賦值。(默認移動賦值跟上面移動構造 完全類似)
  • 如果你提供了移動構造或者移動賦值,編譯器不會自動提供拷貝構造和拷貝賦值。
強制生成默認函數的關鍵字default:

C++11可以讓你更好的控制要使用的默認函數。假設你要使用某個默認的函數,但是因為一些原 因這個函數沒有默認生成。比如:我們提供了拷貝構造,就不會生成移動構造了,那么我們可以 使用default關鍵字顯示指定移動構造生成。

class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}Person(const Person& p):_name(p._name),_age(p._age){}Person(Person&& p) = default;private:bit::string _name;int _age;
};int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0;
}
禁止生成默認函數的關鍵字delete:

如果能想要限制某些默認函數的生成,在C++98中,是J將該函數設置成private,并且只聲明補不定義,這樣只要其他人想要調用就會報錯。在C++11中更簡單,只需在該函數聲明加上=delete即 可,該語法指示編譯器不生成對應函數的默認版本,稱=delete修飾的函數為刪除函數。

class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}Person(const Person& p) = delete;
private:bit::string _name;int _age;
};
int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0;
}

三、可變模版參數

C++11的新特性可變參數模板能夠讓您創建可以接受可變參數的函數模板和類模板,相比 C++98/03,類模版和函數模版中只能含固定數量的模版參數,可變模版參數無疑是一個巨大的改 進。由于可變模版參數比較抽象難懂,本篇博客主要講解其基本特性和使用方法

下面就是一個基本可變參數的函數模板:

// Args是一個模板參數包,args是一個函數形參參數包
// 聲明一個參數包Args...args,這個參數包中可以包含0到任意個模板參數。
template <class ...Args>
void ShowList(Args... args)
{}

上面的參數args前面有省略號,所以它就是一個可變模版參數,我們把帶省略號的參數稱為“參數 包”,它里面包含了0到N(N>=0)個模版參數。我們無法直接獲取參數包args中的每個參數的, 只能通過展開參數包的方式來獲取參數包中的每個參數,這是使用可變模版參數的一個主要特 點,也是最大的難點,即如何展開可變模版參數。由于語法不支持使用args[i]這樣方式獲取可變參數,所以我們的用一些奇招來一一獲取參數包的值。

遞歸函數方式展開參數包
template<class T>
void _Show_list(const T& val)
{cout << val<<endl;
}template<class T,class ...Args>
void _Show_list(T val,Args... args)
{cout << val << " ";_Show_list(args...);
}template<class ...Args>
void Show_list(Args... args)
{_Show_list(args...);
}int main()
{Show_list(1);Show_list(1,"abc");Show_list(1,"abc",2.2);return 0;
}

底層解釋:

逗號表達式展開參數包

這種展開參數包的方式,不需要通過遞歸終止函數,是直接在expand函數體中展開的, printarg 不是一個遞歸終止函數,只是一個處理參數包中每一個參數的函數。這種就地展開參數包的方式 實現的關鍵是逗號表達式。我們知道逗號表達式會按順序執行逗號前面的表達式。 expand函數中的逗號表達式:(printarg(args), 0),也是按照這個執行順序,先執行 printarg(args),再得到逗號表達式的結果0。同時還用到了C++11的另外一個特性——初始化列 表,通過初始化列表來初始化一個變長數組, {(printarg(args), 0)...}將會展開成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc... ),最終會創建一個元素值都為0的數組int arr[sizeof... (Args)]。由于是逗號表達式,在創建數組的過程中會先執行逗號表達式前面的部分printarg(args) 打印出參數,也就是說在構造int數組的過程中就將參數包展開了,這個數組的目的純粹是為了在 數組構造的過程展開參數包

template<class T>
void print(const T& val)
{cout << val << " ";
}template<class ...Args>
void Show_list(Args... args)
{int arr[] = { (print(args),0)... };cout << endl;
}int main()
{Show_list(1,"abc",2.2);return 0;
}
STL容器中的empalce相關接口函數:
template <class... Args>
void emplace_back (Args&&... args);

可見emplace系列使用了可變模版參數和萬能引用,相比于emplace系列,push_back的參數存在左值引用和右值引用兩個版本,那emplace與push_back有什么區別嗎?

如果插入的數據是一個對象的話,不論是傳左值還是傳右值,兩者的效率是一摸一樣的:

emplace系列函數可以直接傳構造對象的參數包,該參數包會一直往下傳,到最底層時直接構造,相比于push_back傳右值來說省了一次移動構造的消耗

總結:

1.對于深拷貝的類,push_back傳右值和emplace傳構造對象的參數包來說,效率差不多,因為對于深拷貝來說,多一次數據交換的影響不大

2.對于淺拷貝的類來說,push_back傳右值要經歷構造和拷貝構造,emplace傳構造對象的參數包僅一次構造,此時效率提升比較大

3.推薦用emplace系列函數代替push和insert系列函數,此外emplace系列函數能傳參數包就傳參數包

四、包裝器

function包裝器

對于可調用對象一般有三種:函數指針、仿函數、lambda表達式,function包裝器 也叫作適配器。C++中的function本質是一個類模板,也是一個包裝器。包裝器一般就是包裝這三種類型的。

std::function在頭文件<functional>// 類模板原型如下
template <class T> function; ? ? // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;模板參數說明:
Ret: 被調用函數的返回類型
Args…:被調用函數的形參
包裝器的使用
// 使用方法如下:
#include <functional>
int f(int a, int b)
{return a + b;
}
struct Functor
{
public:int operator() (int a, int b){return a + b;}
};
class Plus
{
public:static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;}
};
int main()
{// 函數名(函數指針)std::function<int(int, int)> func1 = f;cout << func1(1, 2) << endl;// 函數對象std::function<int(int, int)> func2 = Functor();cout << func2(1, 2) << endl;// lamber表達式std::function<int(int, int)> func3 = [](const int a, const int b){return a + b; };cout << func3(1, 2) << endl;// 類的成員函數std::function<int(int, int)> func4 = &Plus::plusi;cout << func4(1, 2) << endl;std::function<double(Plus, double, double)> func5 = &Plus::plusd;cout << func5(Plus(), 1.1, 2.2) << endl;std::function<double(Plus*, double, double)> func6 = &Plus::plusd;Plus plus;cout << func6(&plus, 1.1, 2.2) << endl;return 0;
}
注意:

包裝器也可以包裝類成員函數,不過要添加函數所屬類域,在c++中一般還要在類名前加一個&,對于靜態成員函數,按照函數聲明傳參即可,對于非靜態成員函數,要注意函數存在一個隱藏的this指針,我們可以傳一個對象,也可以傳一個對象的地址,底層會根據這個對象或者地址從而找到這個函數

包裝器的意義
#include<iostream>
using namespace std;template<class F, class T>
T useF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;return f(x);
}double f(double i)
{return i / 2;
}struct Functor
{double operator()(double d){return d / 3;}
};int main()
{// 函數名cout << useF(f, 11.11) << endl;cout << endl;// 函數對象cout << useF(Functor(), 11.11) << endl;cout << endl;// lamber表達式cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;cout << endl;return 0;
}

通過上面的程序驗證,我們會發現useF函數模板實例化了三份,如此豐富的可調用類型,可能會導致模板的效率低下。而包裝器就可以很好的解決上述問題。

int main()
{// 函數名std::function<double(double)> func1 = f;cout << useF(func1, 11.11) << endl;cout << endl;// 函數對象std::function<double(double)> func2 = Functor();cout << useF(func2, 11.11) << endl;cout << endl;// lamber表達式std::function<double(double)> func3 = [](double d)->double { return d /4; };cout << useF(func3, 11.11) << endl;cout << endl;return 0;
}

這樣useF就僅實例化出一份,比較節省空間

bind綁定

std::bind函數定義在頭文件中,是一個函數模板,它就像一個函數包裝器(適配器),接受一個可 調用對象(callable object),生成一個新的可調用對象來“適應”原對象的參數列表。一般而 言,我們用它可以把一個原本接收N個參數的函數fn,通過綁定一些參數,返回一個接收M個(M 可以大于N,但這么做沒什么意義)參數的新函數。同時,使用std::bind函數還可以實現參數順 序調整等操作。

// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

可以將bind函數看作是一個通用的函數適配器,它接受一個可調用對象,生成一個新的可調用對 象來“適應”原對象的參數列表。 調用bind的一般形式:auto newCallable = bind(callable,arg_list); 其中,newCallable本身是一個可調用對象,arg_list是一個逗號分隔的參數列表,對應給定的 callable的參數。當我們調用newCallable時,newCallable會調用callable,并傳給它arg_list中 的參數。

通過bind改變函數的參數順序

arg_list中的參數可能包含形如_n的名字,其中n是一個整數,這些參數是“占位符”,表示 newCallable的參數,它們占據了傳遞給newCallable的參數的“位置”。數值n表示生成的可調用對 象中參數的位置:_1為newCallable的第一個參數,_2為第二個參數,以此類推。這些占位符存在placehoders的命名空間中

通過bind改變函數的傳參個數
void func(string s, int a, int b)
{cout << s << " " << a << " " << b << endl;
}int main()
{auto f1 = bind(func,"abc" ,placeholders::_1, placeholders::_2);f1(1, 2);auto f2 = bind(func, placeholders::_1,666, placeholders::_2);f2("def", 1);
}結果:
//abc 1 2
//def 666 1

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/39083.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/39083.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/39083.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

自動備份Docker中的mysql數據庫

先說一下&#xff0c;在Linux下備份mysql 1、先創建一個腳本文件 #!/bin/bash # MySQL 用戶、密碼、數據庫名稱 DB_USER"dbuser" DB_PASSWORD"dbpassword" DATABASE"mydatabase" # 創建備份目錄 BACKUP_DIR"/path/to/your/backup/dire…

化身李時珍弟子,演繹中醫藥故事,李良濟花神戲,創新傳承中醫藥文化

6月29日&#xff0c;李良濟與花神戲聯袂舉辦的兒童劇本&#xff0c;在李良濟嵩山店強勢開啟。 20余名小朋友&#xff0c;一起在這次中醫藥兒童劇本活動中&#xff0c;化身李時珍弟子&#xff0c;學中醫&#xff0c;識草藥&#xff0c;傳承中醫智慧&#xff0c;沉浸式學習傳統文…

Spring Boot與Apache Kafka的深度集成

Spring Boot與Apache Kafka的深度集成 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;今天我們將探討如何在Spring Boot應用中實現與Apache Kafka的深度集成&am…

關于一維,二維正態分布的繪制

繪制一維正態分布代碼 % 給定的均值和標準差 mu 0; % 例如&#xff0c;你可以改變這個值 sigma 1; % 例如&#xff0c;你可以改變這個值 % 定義x的范圍&#xff08;例如&#xff0c;從mu-3*sigma到mu3*sigma&#xff0c;步長為0.1&#xff09; x mu - 3*sigma:0.1:m…

STM32 中斷編程入門

目錄 一、中斷系統 1、中斷的原理 2、中斷類型 外部中斷 定時器中斷 DMA中斷 3、中斷處理函數 中斷標志位清除 中斷服務程序退出 二、實際應用 中斷控制LED 任務要求 代碼示例 中斷控制串口通信 任務要求1 代碼示例 任務要求2 代碼示例 總結 學習目標&…

ROS學習筆記(17):建圖與定位(1)

目錄 0.前言 1.定位和建圖 1.里程計&#xff08;Odometry&#xff09; 2.掃描匹配&#xff08;Scan Matching&#xff09; 3.結尾 0.前言 好久不見各位&#xff0c;前段時間忙著考試&#xff08;6級和一些專業課&#xff09;和擺爛斷更了近30天&#xff0c;現在哥們回來更…

計算機畢業設計Python+Spark股票基金推薦與預測系統 股票基金可視化 股票基金推薦系統 股票基金可視化系統 股票基金數據分析 股票基金爬蟲大數據

目 錄 摘 要 Abstract 第1章 前 言 1.1 項目的背景和意義 1.2 研究現狀 1.3 項目的目標和范圍 1.4 論文結構簡介 第2章 技術與原理 2.1 開發原理 2.2 開發工具 2.3 關鍵技術 第3章 需求建模 3.1 系統可行性分析 3.2 功能需求分析 3.3 非功能性…

C++Primer Plus 第十四章代碼重用:編程練習,第一題

CPrimer Plus 第十四章代碼重用&#xff1a;編程練習,第一題 提示&#xff1a;這里可以添加系列文章的所有文章的目錄&#xff0c;目錄需要自己手動添加 CPrimer Plus 第十四章代碼重用&#xff1a;編程練習,第一題 提示&#xff1a;寫完文章后&#xff0c;目錄可以自動生成&am…

高職人工智能專業實訓課之“生成對抗網絡(GAN)”

一、前言 生成對抗網絡&#xff08;GAN&#xff09;作為人工智能領域的一項重要技術&#xff0c;已經在圖像生成、風格遷移、數據增強等多個領域展現出巨大的潛力和應用價值。為了滿足高職院校對GAN專業實訓課程的需求&#xff0c;唯眾人工智能教學實訓憑借其前沿的教育技術平…

mst[講課留檔]

最小生成樹(Minimum Spanning Tree) (1)概念 我們知道&#xff0c;樹是有 n n n個結點&#xff0c; n ? 1 n-1 n?1條邊的無向無環的連通圖。 一個連通圖的生成樹是一個極小的連通子圖&#xff0c;它包含圖中全部的 n n n個頂點&#xff0c;但只有構成一棵樹的 n ? 1 n-1 …

內容營銷專家劉鑫煒:越是賺不到錢,越要加大推廣力度

這兩天&#xff0c;一位跟我們有長期合作關系的小微企業主老蘇問我。 “現在錢這么不好賺&#xff0c;品牌推廣應該怎么做&#xff1f;” 我說&#xff1a;“這是好機會&#xff0c;加大投放力度&#xff01;” 老蘇很是不解&#xff0c;這時候不開源節流&#xff0c;還要加…

使用Git從Github上克隆倉庫,修改并提交修改

前言 本次任務主要是進行github提交修改的操作練習實踐&#xff0c;本文章是對實踐過程以及遇到的問題進行的一個記錄。 在此之前&#xff0c;我已經簡單使用過github&#xff0c;Git之前已經下好了&#xff0c;所以就省略一些步驟。 步驟記錄 注冊github賬號&#xff0c;gi…

【C++】C++指針在線程中調用與受保護內存空間讀取方法

引言 在C的多線程編程中&#xff0c;正確地管理內存和同步訪問是確保程序穩定性和安全性的關鍵。特別是當涉及到指針在線程中的調用時&#xff0c;對受保護內存空間的訪問必須謹慎處理&#xff0c;以防止數據競爭、死鎖和內存損壞等問題。本文將詳細探討C指針在線程中調用時如何…

理解 React 的嚴格模式

文章目錄 有什么優劣優點&#xff1a;缺點&#xff1a; 使用場景如何使用為整個應用啟用嚴格模式一部分代碼啟用嚴格模式 React 的 Strict Mode&#xff08;嚴格模式&#xff09;是一種用于檢測應用中潛在問題的開發工具。它不會渲染任何可見的 UI 元素&#xff0c;而是通過激活…

element-ui如何做表單驗證

Element UI 使用表單驗證通常涉及兩個主要組件&#xff1a;el-form 和 el-form-item。 el-form 負責管理表單數據和驗證規則&#xff0c;而 el-form-item 用于定義需要驗證的表單項。 <template><el-form :model"form" :rules"rules" ref"fo…

易校網校園綜合跑腿小程序源碼修復運營版

簡介&#xff1a; 易校網校園綜合跑腿小程序源碼修復運營版&#xff0c;帶服務端客戶端前端文檔說明。 源碼安裝方法&#xff1a; 需要準備小程序服務號 服務器 備案域名 校園網跑腿小程序源碼需要準備 1.小程序 2.服務器&#xff08;推薦配置2h4g3m&#xff09; 3.域名…

使用JMeter+Grafana+Influxdb搭建可視化性能測試監控平臺

【背景說明】 使用jmeter進行性能測試時&#xff0c;工具自帶的查看結果方式往往不夠直觀和明了&#xff0c;所以我們需要搭建一個可視化監控平臺來完成結果監控&#xff0c;這里我們采用三種JMeterGrafanaInfluxdb的方法來完成平臺搭建 【實現原理】 通過influxdb數據庫存儲…

開源模型應用落地-FastAPI-助力模型交互-WebSocket篇(五)

一、前言 使用 FastAPI 可以幫助我們更簡單高效地部署 AI 交互業務。FastAPI 提供了快速構建 API 的能力,開發者可以輕松地定義模型需要的輸入和輸出格式,并編寫好相應的業務邏輯。 FastAPI 的異步高性能架構,可以有效支持大量并發的預測請求,為用戶提供流暢的交互體驗。此外,F…

【圖論】樹鏈剖分

樹鏈剖分詳解 - 自為風月馬前卒 - 博客園 (cnblogs.com) P3384 【模板】重鏈剖分/樹鏈剖分 - 洛谷 | 計算機科學教育新生態 (luogu.com.cn) #include<iostream> using namespace std;void dfs1(int u,int father){ fa[u]father; dep[u]dep[father]1; sz[u]1;for(int ih…

SpringBoot中整合ONLYOFFICE在線編輯

SpringBoot整合OnlyOffice SpringBoot整合OnlyOffice實現在線編輯1. 搭建私有的OnlyOffice的服務2. SpringBoot進行交互2.1 環境2.2 我們的流程2.3 接口規劃2.3.1 獲取編輯器配置的接口2.3.2 文件下載地址2.3.3 文件下載地址 3. 總結4. 注意4.1 你的項目的地址一定一定要和only…