【C++心愿便利店】No.13---C++之探索vector底層原理

文章目錄

  • 前言
  • 一、STL簡介
    • 1.1 什么是STL
    • 1.2 STL的六大組件
  • 二、vector的介紹及使用
    • 2.1 vector的介紹
    • 2.2 vector的使用
      • 2.2.1 vector的定義
      • 2.2.2 vector iterator 的使用
      • 2.2.3 vector 空間增長問題
      • 2.2.4 vector 增刪查改
  • 三、vector模擬實現
    • 3.1 成員變量
    • 3.2 成員函數
      • 3.2.1 構造函數
      • 3.2.2 拷貝構造函數
      • 3.2.3 operator=
      • 3.2.4 size
      • 3.2.5 capacity
      • 3.2.6 reserve(注意memcpy的拷貝方式)
      • 3.2.7 resize
      • 3.2.8 operator[]
      • 3.2.9 insert(涉及迭代器失效)
      • 3.2.10 erase(涉及迭代器失效)
      • 3.2.11 push_back
      • 3.2.12 pop_back
      • 3.2.13 迭代器


前言

在這里插入圖片描述

👧個人主頁:@小沈YO.
😚小編介紹:歡迎來到我的亂七八糟小星球🌝
📋專欄:C++ 心愿便利店
🔑本章內容:vector
記得 評論📝 +點贊👍 +收藏😽 +關注💞哦~


提示:以下是本篇文章正文內容,下面案例可供參考

一、STL簡介

1.1 什么是STL

STL(standard template libaray-標準模板庫):是C++標準庫的重要組成部分,不僅是一個可復用的組件庫,而且是一個包羅數據結構與算法的軟件框架

1.2 STL的六大組件

在這里插入圖片描述

二、vector的介紹及使用

2.1 vector的介紹

vector的文檔介紹

  • vector是表示可變大小數組的序列容器
  • 就像數組一樣,vector也采用連續存儲空間來存儲元素。也就是意味著可以采用下標對vector的元素進行訪問,和數組一樣高效。但是又不像數組,它的大小是可以動態改變的,而且它的大小會被容器自動處理
  • 本質講,vector使用動態分配數組來存儲它的元素。當新元素插入時候,這個數組需要被重新分配大小為了增加存儲空間。其做法是,分配一個新的數組,然后將全部元素移到這個數組。就時間而言,這是一個相對代價高的任務,因為每當一個新的元素加入到容器的時候,vector并不會每次都重新分配大小。
  • vector分配空間策略:vector會分配一些額外的空間以適應可能的增長,因為存儲空間比實際需要的存儲空間更大。不同的庫采用不同的策略權衡空間的使用和重新分配。但是無論如何,重新分配都應該是對數增長的間隔大小,以至于在末尾插入一個元素的時候是在常數時間的復雜度完成的。
  • 因此,vector占用了更多的存儲空間,為了獲得管理存儲空間的能力,并且以一種有效的方式動態增長。
  • 與其它動態序列容器相比(deque, list and forward_list), vector在訪問元素的時候更加高效,在末尾添加和刪除元素相對高效。對于其它不在末尾的刪除和插入操作,效率更低。比起list和forward_list統一的迭代器和引用更好。

2.2 vector的使用

2.2.1 vector的定義

(constructor)構造函數聲明接口說明
vector()(重點)無參構造
vector(size_type n, const value_type& val = value_type())構造并初始化n個val
vector (const vector& x); (重點)拷貝構造
vector (InputIterator first, InputIterator last);使用迭代器進行初始化構造
void test_vector1()
{//構造函數vector<int> v;//無參構造vector<int> v1(5, 1);vector<int> v2(v1.begin(), v1.end());string s1 = "hello world";vector<int> v3(s1.begin(), s1.end());//隱式類型轉換vector<int> v4(v3);//拷貝構造
}

2.2.2 vector iterator 的使用

iterator的使用接口說明
begin + end(重點)獲取第一個數據位置的iterator/const_iterator,獲取最后一個數據的下一個位置的iterator/const_iterator
rbegin + rend獲取最后一個數據位置的reverse_iterator,獲取第一個數據前一個位置的reverse_iterator

在這里插入圖片描述

void test_vector1()
{vector<int> v2(v1.begin(), v1.end());string s1 = "hello world";vector<int> v3(s1.begin(), s1.end());//隱式類型轉換vector<int> v4(v3);//拷貝構造//1.iteratorvector<int>::iterator it = v3.begin();while (it != v3.end()){cout << *it << " ";it++;}cout << endl;//2.operator[]for (size_t i = 0; i < v2.size(); i++){cout << v2[i] << " ";}cout << endl;//3.范圍forfor (auto e : v4){cout << e << " ";}cout << endl;
}

2.2.3 vector 空間增長問題

容量空間接口說明
size獲取數據個數
capacity獲取容量大小
empty判斷是否為空
resize(重點)改變vector的size
reserve (重點)改變vector的capacity
// 測試vector的默認擴容機制
void TestVectorExpand()
{size_t sz;vector<int> v;sz = v.capacity();cout << "making v grow:\n";for (int i = 0; i < 100; ++i) {v.push_back(i);if (sz != v.capacity()) {sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}
}
1 .VS下的結果:capacity是按1.5倍增長的

請添加圖片描述

2 .Linux下的結果g++:是按2倍增長的

在這里插入圖片描述

  • capacity的代碼在vs和g++下分別運行會發現,vs下capacity是按1.5倍增長的,g++是按2倍增長的。這個問題經常會考察,不要固化的認為,vector增容都是2倍,具體增長多少是根據具體的需求定義的。vs是PJ版本STL,g++是SGI版本STL。

  • reserve只負責開辟空間,如果確定知道需要用多少空間,reserve可以緩解vector增容的代價缺陷問題。

// 如果已經確定vector中要存儲元素大概個數,可以提前將空間設置足夠
// 就可以避免邊插入邊擴容導致效率低下的問題了
void TestVectorExpandOP()
{vector<int> v;size_t sz = v.capacity();v.reserve(100); // 提前將容量設置好,可以避免一遍插入一遍擴容cout << "making bar grow:\n";for (int i = 0; i < 100; ++i) {v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}
}
  • resize在開空間的同時還會進行初始化,影響size
void test_vector3()
{vector<int> v1;cout << v1.max_size() << endl;size_t sz;vector<int> v;//v.reserve(100);   ---> size=0,capacity=100v.resize(100);      ---> size=100,capacity=100//for (size_t i = 0; i < v.size(); i++)//當寫成這種形式用reserve(100)是不會進入循環的因為v.size()返回值是0for (size_t i = 0; i < 100; i++){v[i] = i;//斷言在release下不起作用//雖然空間開出來了100但是不能訪問,因為operator[]里面加了斷言,斷言訪問的下標必須是小于size的,大于size就越界了,所以只能訪問[0,size-1]的數據,但是reverse(100)--->size=0}for (auto e : v){cout << e << " ";}cout << endl;
}

上面的代碼用reserve(100)雖然給 v 提前開了 100 個空間,但是 v 中的有效元素個數size還是 0,所以不能直接通過下標去訪問 vector 對象中的每一個元素,因為 operator[ ] 實現中的第一步就是檢查下標的合理性,防止越界訪問,執行 assert(pos < _size),而此時 _size 是 0,就會越界報錯。而把 reserve 改成 resize 就可以正常運行,因為 resize 會改變 _size 的大小。
在這里插入圖片描述

2.2.4 vector 增刪查改

vector增刪查改接口說明
push_back(重點)尾插
pop_back (重點)尾刪
find查找 - - -(注意這個是算法模塊實現,不是vector的成員接口)
insert在position之前插入val
erase刪除position位置的數據
swap交換兩個vector的數據空間
operator[] (重點)像數組一樣訪問

在這里插入圖片描述

vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.insert(v.begin(), 0);auto it=find(v.begin(), v.end(), 3);尋找--->注意這個是算法模塊實現,不是vector的成員接口if (it != v.end()){v.insert(it, 30);插入}for (auto e : v){cout << e << " ";}cout << endl;it = find(v.begin(), v.end(), 3);if (it != v.end()){v.erase(it);刪除}for (auto e : v){cout << e << " ";}cout << endl;cout << v.size() << endl;cout << v.capacity() << endl;v.clear();v.shrink_to_fit();//縮容不建議用cout << v.size() << endl;cout << v.capacity() << endl;
}

在這里插入圖片描述

三、vector模擬實現

在這里插入圖片描述

3.1 成員變量

class vector
{
public:typedef T* iterator;typedef const T* const_iterator;
private:iterator _start;iterator _finish;iterator _end_of_storage;
};

3.2 成員函數

3.2.1 構造函數

//無參構造
vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}
//帶參構造并初始化
vector(int n, const T& val  = T())
{reserve(n);for (int i = 0; i < n; i++){push_back(val);}
}vector(size_t n, const T& val = T())
{rserve(n);for (size_t i = 0; i < n; i++){push_back(val);}
}
//迭代器區間初始化
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);first++;}
}
  • 無參的構造并不陌生
  • 帶參的構造并初始化為什么要寫兩個?

因為如果不單獨提供一個 vector(int n, const T& val = T());代碼vector v1(10, 1) 會走最匹配的,即和迭代器區間初始化函數匹配(迭代器區間初始化采用的是函數模板),但是我們希望它走 vector(size_t n, const T& val = T()) 構造函數,可是 10 是 int 型,和 size_t 不匹配上(只會走最匹配的),因此就會去和迭代器區間初始化函數進行匹配,InputIterator 就會被實例化成 int 型,函數中會對 int 型解引用,就會報錯

  • 迭代器區間初始化采用的是函數模板,因為它可能使用不同類型的迭代器。

3.2.2 拷貝構造函數

vector(const vector<T>& v)//const對象:_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{reserve(v.capacity());for (auto& e : v)//這里加上&防止T是個大對象拷貝代價大{push_back(e);}
}

3.2.3 operator=

vector<T> operator=(vector<T> tmp)
{swap(tmp);return *this;
}
void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}

3.2.4 size

size_t size() const
{return _finish - _start;
}

3.2.5 capacity

size_t capacity() const
{return _end_of_storage - _start;
}

3.2.6 reserve(注意memcpy的拷貝方式)

void reserve(size_t n)
{if (n > capacity()){T* tmp = new T[n];if (_start){memcpy(tmp, _start, sizeof(T) * sz);delete[] _start;}_start = tmp;_finish = _start + size();_end_of_storage = _start + cp;}
}
_________________________________________________________________________________________
void reserve(size_t n)
{if (n > capacity()){T* tmp = new T[n];if (_start){memcpy(tmp, _start, sizeof(T) * sz);delete[] _start;}_finish = tmp + size();_start = tmp;_end_of_storage = _start + cp;}
}
_________________________________________________________________________________________
void reserve(size_t n)
{if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){memcpy(tmp, _start, sizeof(T) * sz);delete[] _start;}_start = tmp;_finish = _start + sz;_end_of_storage = _start + n;}
}

注意:第一種代碼的寫法是不正確的,因為當開辟新空間tmp,將_start中的數據拷貝到tmp中,釋放原空間,將_start指針指向新空間,所以運行代碼_finish = _start + size();時要先調用size(),而size=_finish - _start,其中_finish是nullptr(構造時初始化),而_start并不是nullptr(此時是tmp),所以我們預期中的size并不是等于0,而是size=0-_start,所以_finish=_start+0-_start=0
解決方式

  • 寫成_finish = tmp + size(); _start = tmp;注意先后順序,如果寫成 _start = tmp; _finish = tmp + size(); 先調用size=_finish-_start=0-tmp;最終_finish=tmp+0-tmp還是沒有解決問題,同樣也不能寫成_finish = _start + size(); _start = tmp; 此時的_start=0,所以_finish=0+0=0,也是不對的
  • 第一種的解決方式是可以的但是可讀性太差,順序不對就導致錯誤,所以可以提前用一個變量存儲size()的返回值,size_t sz = size();此時就不會因為順序報錯啦

上面這種擴容邏輯,當 T 是內置類或者是不需要進行深拷貝的自定義類型來說,是可以的。但是當 T 是需要進行深拷貝的類型時,上述這種擴容方式就會出現問題。下述代碼以 vector 為例:

void reserve(size_t n)
{if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){memcpy(tmp, _start, sizeof(T) * sz);delete[] _start;}_start = tmp;_finish = _start + sz;_end_of_storage = _start + n;}
}
void test_vector5()
{vector<string> s;s.push_back("11111111111111111111");s.push_back("11111111111111111111");s.push_back("11111111111111111111");s.push_back("11111111111111111111");s.push_back("11111111111111111111");for (auto e : s){cout << e << " ";}cout << endl;
}

在這里插入圖片描述
通過上圖可以明顯地觀察到當插入4個字符串時是沒有問題的但是當插入第五個字符串時就會出現問題,顯然是擴容中出現了問題,上述代碼用 memcpy 將舊空間的數據拷貝到新空間,那么新舊空間中存儲的 string 對象指向同一個堆區上的字符串空間,接著在執行 delete[] _start; 銷毀舊空間的時候,由于該 _start 是一個 string* 的指針,所以會先調用 string 的析構函數,將對象中申請的空間釋放,也就是釋放 _str 指向的空間,然后再去調用 operator delete 函數釋放 string 對象的空間。所以新空間中存儲的 string 對象就出現了問題,因為它的成員變量 _str 指向的空間已經被釋放了。
顯然 memcpy 執行的是淺拷貝,只需要修改成tmp[i] = _start[i]; 這樣會調用 string 對象的賦值運算重載,進行深拷貝。

void reserve(size_t n)
{if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){//memcpy(tmp, _start, sizeof(T) * sz);for (size_t i=0;i<sz;i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_end_of_storage = _start + n;}
}

3.2.7 resize

void resize(size_t n, const T& val = T())
{if (n <= size()){_finish = _start + n;}else{reserve(n);//填數據while (_finish<_start+n){*_finish = val;++_finish;}}
}

3.2.8 operator[]

//讀寫
T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}
//只能讀
const T& operator[](size_t pos) const
{assert(pos < size());return _start[pos];
}

3.2.9 insert(涉及迭代器失效)

void insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);//檢查容量}iterator end = _finish - 1;//插入數據while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;
}
___________________________________________________________________________________________-
void insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_storage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;
}

注意:在進行 insert 的時候,會引發一個問題 ---- 迭代器失效:如果失效就不能再使用這個迭代器,如果使用了,結果未定義。
首先pos 是一個迭代器,在 pos 位置插入一個數據時,要在插入數據之前先檢查容量,進行擴容,但是執行了擴容邏輯后,_start、_finish、_end_of_storage 都指向了新空間,舊空間被釋放了,且 pos 指向的卻還是原來空間中的某個位置,此時 pos 就變成了野指針,再去 pos 指向的位置插入數據時,就會造成非法訪問就像第一種所寫的代碼(總而言之就是擴容導致pos失效
解決方式:為了解決迭代器失效的問題,可以在檢查容量擴容之前先保存一下pos的相對位置,在進行擴容釋放舊空間指向新空間邏輯后,更新一下pos指針(總而言之就是更新pos
在這里插入圖片描述

iterator insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_storage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;return pos
}
void test_vector3()
{vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);vector<int>::iterator it = v.begin()+2;v.insert(it, 30);for (auto e : v){cout << e << " ";}cout << endl;
}

對于上述代碼調用insert,雖然更新了 pos,但是由于是傳值形參 pos 的更新,并不會改變實參的 it,所以insert后it可能會失效。
解決上述問題很簡單直接把形參的 pos 變成引用不就可以嗎?這樣形參pos的改變也就使得實參it改變。
這樣是可以的解決了傳值傳參的問題,但是也會引發諸多問題,例如:v.insert(v.begin(), 30);當運行這段代碼時就會報錯,因為實參具有常性(begin()是傳值返回,會產生一個臨時變量,臨時變量具有常性),&必須要傳變量不能傳帶const屬性的值
所以形參 pos 用引用的話,就需要加 const 進行修飾。但是如果用 const 進行修飾,那在函數內部就不能對 pos 進行更新所以形參 pos 不能用引用,可以采用返回值的方式,將更新后的 pos 返回。

3.2.10 erase(涉及迭代器失效)

iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish){*(it - 1) = *it;it++;}_finish--;return pos
}

根據上述insert實現中迭代器失效是因為擴容引起的,但是erase 只是刪除 pos 位置元素,pos 位置之后的元素往前覆蓋,沒有導致空間的改變理論上迭代器不會失效
但是根據上述代碼,如果 pos 剛好是最后一個元素,刪完之后 pos 剛好是 _finish 的位置,而 _finish 位置是沒有元素的,那么 pos 就失效了。為了迭代器失效問題,還是可以采用返回值的方式,返回 pos 下一個位置元素的迭代器。

3.2.11 push_back

void push_back(const T& x)
{if (_finish == _end_of_storage){reserve( capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;            _finish++;                
}
__________________________________________________________________________________
//push_back可以直接復用insert
void push_back(const T& x)
{insert(end(), x);
}

3.2.12 pop_back

void pop_back()
{erase(--end());
}

3.2.13 迭代器

iterator begin()
{return _start;
}iterator end()
{return _finish;
}const_iterator begin() const
{return _start;
}
const_iterator end() const
{return _finish;
}vector<int>::iterator it = v.begin();
while (it != v.end())
{*it *= 10;cout << *it << " ";it++;
}
cout << endl;
for (auto e : v)
{cout << e << " ";
}cout << endl;
}

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

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

相關文章

2、分布式鎖實現原理與最佳實踐(二)

常見分布式鎖的原理 4.1 Redisson Redis 2.6之后才可以執行lua腳本&#xff0c;比起管道而言&#xff0c;這是原子性的&#xff0c;模擬一個商品減庫存的原子操作&#xff1a; //lua腳本命令執行方式&#xff1a;redis-cli --eval /tmp/test.lua , 10 jedis.set("produ…

python opencv 放射變換和圖像縮放-實現圖像平移旋轉縮放

python opencv 放射變換和圖像縮放-實現圖像平移旋轉縮放 我們實現這次實驗主要用到cv2.resize和cv2.warpAffine cv2.warpAffine主要是傳入一個圖像矩陣&#xff0c;一個M矩陣&#xff0c;輸出一個dst結果矩陣&#xff0c;計算公式如下&#xff1a; cv2.resize則主要使用fx&…

精益生產中的周轉箱優勢:提升效率與質量的得力利器

在當今競爭激烈的制造業中&#xff0c;企業追求高效生產和卓越質量是至關重要的。精益生產理念提供了一套有效的工具和方法&#xff0c;其中周轉箱作為一個關鍵的組成部分&#xff0c;在優化生產流程、提高效率和質量方面發揮著重要作用。下面談談精益生產中的周轉箱優勢&#…

C++:內存管理

內存分布&#xff1a; 首先我們需要了解的是C/C中內存區域的劃分&#xff1a; 1. 棧又叫堆棧--非靜態局部變量/函數參數/返回值等等&#xff0c;棧是向下增長的&#xff1a;先調用的地址比后調用的地址大。 2. 內存映射段是高效的I/O映射方式&#xff0c;用于裝載一個共享的動…

百度文心一言(千帆大模型)聊天API使用指導

開篇不得不吐槽下百度&#xff0c;百度智能云平臺首頁跳轉千帆大模型平臺的按鈕太多了&#xff0c;不同按鈕跳轉不同的子頁面&#xff0c;不熟悉的&#xff0c;能把人找懵。入口太多&#xff0c;就導致用戶不知道從何開始。本文就從一個前端開發人員的角度&#xff0c;教大家快…

【深度學習】基于深度學習的超分辨率圖像技術一覽

超分辨率(Super-Resolution)即通過硬件或軟件的方法提高原有圖像的分辨率&#xff0c;圖像超分辨率是計算機視覺和圖像處理領域一個非常重要的研究問題&#xff0c;在醫療圖像分析、生物特征識別、視頻監控與安全等實際場景中有著廣泛的應用。 SR取得了顯著進步。一般可以將現有…

為什么,word文件在只讀模式下,仍然能編輯?

Word文檔設置了只讀模式&#xff0c;是可以編輯的&#xff0c;但是當我們進行保存的時候就會發現&#xff0c;word提示需要重命名并選擇新路徑才能夠保存。 這種操作&#xff0c;即使可以編輯文字&#xff0c;但是原文件是不會受到影響的&#xff0c;編輯之后的word文件會保存到…

torch常用和預期輸入輸出

import torch import torch.nn as nn import torch.nn.functional as F nn中定義的是類&#xff0c;functional里面定義的是函數操作。 輸出shape的計算公式&#xff1a; o u t _ s h a p e r o u n d _ m o d e ( i n _ s h a p e 2 ? p a d d i n g ? k e r n e l _ s…

20231124給RK3399的挖掘機開發板在Andorid10下加鼠標右鍵返回

20231124給RK3399的挖掘機開發板在Andorid10下加鼠標右鍵返回 2023/11/24 12:19 百度&#xff1a;RK3399 Android10 右鍵返回 https://blog.csdn.net/danhu/article/details/122467256 android9/android10 鼠標右鍵返回(已驗證) danhu 于 2022-01-13 09:46:42 發布 android10 …

Echarts 大屏注冊自定義地圖解析文件流報錯問題解決

效果圖: 1、首先通過后臺接口獲取到SVG圖片的文件流,postman能夠正確解析出文件流,前端調用api時需要設置返回的響應格式為image/svg+xml格式,否則解析失敗 拿到文件流后是這樣的 <?xml version="1.0" encoding="utf-8"?> <!-- Generator: …

【深度學習】P1 深度學習基礎框架 - 張量 Tensor

深度學習基礎框架 張量 Tensor 張量數據操作導入創建張量獲取張量信息改變張量張量運算 張量與內存 張量 Pytorch 是一個深度學習框架&#xff0c;用于開發和訓練神經網絡模型。 而其核心數據結構&#xff0c;則是張量 Tensor&#xff0c;類似于 Numpy 數組&#xff0c;但是可…

AI制作的《大多數普通女孩的一生》——公開教程和工作流

內容來源&#xff1a;JiamigouCn ?這周由AI制作的《大多數普通女孩的一生》&#xff0c;在抖音爆火&#xff0c;獲得新華網轉發。到目前為止&#xff0c;全網還沒有公開教程和工作流&#xff0c;需要花費800-2000購買。 本著AI社區共享原則&#xff0c;我委托公眾號“楚思智能…

小學生古詩文大會復賽在線模擬新增刷題版和闖關版,幫助孩子沖刺

小學生古詩文大會明天就要開始了&#xff0c;剛剛古詩文大會主辦方也正式發布了通知&#xff0c;總體安排、操作指引和我之前發布的一樣&#xff1a;2023年11月25日小學生古詩文大會復選&#xff08;復賽&#xff09;答題操作手冊 為了幫助參加復選&#xff08;復賽&#xff09…

NFC技術簡介

NFC簡介 NFC(近場通信&#xff0c;Near Field Communication&#xff09;是一種短距高頻的無線電技術&#xff0c;由非接觸式射頻識別(RFID)演變而來。 NFC工作頻率為13.56Hz&#xff0c;通常只有在距離不超過4厘米時才能啟動連接&#xff0c;其傳輸速度有106 Kbit/秒、212 Kb…

從文本生成到數據增強:探索 AI 前沿的開源套件 | 開源專題 No.44

Significant-Gravitas/AutoGPT Stars: 150.4k License: MIT AutoGPT 是開源 AI 代理生態系統的核心工具包。它采用模塊化和可擴展的框架&#xff0c;使您能夠專注于以下方面&#xff1a; 構建 - 為驚人之作打下基礎。測試 - 將您的代理調整到完美狀態。查看 - 觀察進展成果呈…

【Mybatis源碼】反射 - MetaClass

前面我們介紹了Reflector類,Reflector主要完成了Class類中Setter、Getter方法的封裝,可以使用屬性獲取對應的Getter、Setter方法完成方法的調用,同時也可以判斷屬性是否存在,是否存在Getter、Setter方法。 使用Reflector解決了訪問Class類中屬性的問題,但是如果屬性是成員…

HandBrake 1.7 近日發布

導讀HandBrake 1.7 近日發布&#xff0c;作為這個開源、免費和跨平臺視頻轉碼器應用程序的重大更新&#xff0c;適用于 GNU/Linux、macOS 和 Windows 系統。 在 HandBrake 1.6 發布近一年后&#xff0c;HandBrake 1.7 版本為 Linux 用戶提供了許多好處&#xff0c;包括視頻摘要…

C語言第二十八彈--輸入一個非負整數,返回組成它的數字之和

C語言求輸入一個非負整數&#xff0c;返回組成它的數字之和 方法一、遞歸法 思路&#xff1a;設計一個初始條件&#xff0c;通過遞歸獲取非負整數的個位&#xff0c;不斷接近遞歸條件即可。 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h>int DigitSum(int n) {…

PGZ-SBV系列項目介紹、技術選型、技術優勢(AIO內部版)All-In-One

前情提示 項目孵化于2014年,內部正式發行于2015.隨著業務需求,部分內部高級功能逐步對外開放支持多行業(保險、金融、餐飲、旅游、電商、直播等等),多架構,支持AI集成,支持協同,只要你想到的均可集成、均已內置高安全,高可用,多技術解決方案包含av版本、gf低代碼版、…

ubuntu22.04 arrch64版在線安裝maven

腳本 if type -p mvn; thenecho "maven has been installed."elsecd /home/zenglgwget https://dlcdn.apache.org/maven/maven-3/3.9.5/binaries/apache-maven-3.9.5-bin.tar.gz --no-check-certificatetar vxf apache-maven-3.9.5-bin.tar.gz rm -rf /usr/local/mav…