C++ 模擬實現vector

目錄

一、定義

二、模擬實現

1、無參初始化

2、size&capacity

3、reserve

4、push_back

5、迭代器

6、empty

7、pop_back

8、operator[ ]

9、resize

10、insert

迭代器失效問題

11、erase

12、帶參初始化

13、迭代器初始化

14、析構函數

完整版代碼


一、定義

本次參考SGI版本STL中的vector模擬實現。

我們可以看到上述源代碼中,SGI版本vector是借助指針實現的,元素的處理是通過兩個指針來實現的,而不是三個迭代器。這兩個指針分別是_start和_finish。

  • _start指針指向vector中的第一個元素。
  • _finish指針指向vector中最后一個元素的下一個位置。

通過_start和_finish指針,可以確定vector中存儲的元素范圍。

?

此外,SGI版本的vector還使用了一個指針_end_of_storage來表示vector分配的內存空間的末尾位置。

這些指針的使用使得SGI版本的vector能夠高效地進行元素的插入、刪除和訪問操作。

為了不影響VS中STL庫已有的vector,我們選擇將模擬實現的vector放在自定義命名空間中。

namespace byte
{template<class T>class vector{public:private:iterator _start;iterator _finish;iterator _end_of_storage;};
}

二、模擬實現

1、無參初始化

vector():_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{}

2、size&capacity

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

3、reserve

void reserve(size_t n)
{if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){memcpy(tmp, _start, sizeof(T) * size());delete[] _start;}_start = tmp;_finish = _start + sz;_end_of_storage = _start + n;}
}
  1. if (n > capacity()):檢查傳入的n是否大于當前vector的容量。如果是,則需要進行內存重新分配。

  2. size_t sz = size();:保存當前vector的大小(元素個數)。

  3. T* tmp = new T[n];:創建一個新的大小為n的動態數組tmp,用于存儲重新分配后的元素。

  4. if (_start):檢查_start指針判斷舊空間是否為非空。如果_start指針不為空,說明vector中已經有元素存儲在舊的內存空間中。

  5. memcpy(tmp, _start, sizeof(T) * size());:使用memcpy函數將舊的內存空間中的元素復制到新的內存空間tmp中。這樣可以保留元素的值。

  6. delete[] _start;:釋放舊的內存空間。

  7. _start = tmp;:將_start指針指向新的內存空間tmp。

  8. _finish = _start + sz;:更新_finish指針,使其指向新的內存空間中的最后一個元素的下一個位置。

  9. _end_of_storage = _start + n;:更新_end_of_storage指針,使其指向新的內存空間的末尾位置。?

4、push_back

void push_back(const T& x)
{if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;
}
  1. 使用const T& x作為參數類型可以避免不必要的拷貝操作,因為傳入的實參可以直接通過引用訪問,而不需要進行拷貝構造。這可以提高性能和效率,特別是當處理大型對象時。

    另外,使用const T& x還可以確保傳入的元素不會被修改,因為const關鍵字表示傳入的引用是只讀的,函數內部不能修改傳入的對象。

  2. if (_finish == _end_of_storage)?這個條件判斷用于檢查當前vector是否已經達到了內存空間的末尾。如果是,則需要進行內存重新分配。

  3. reserve(capacity() == 0 ? 4 : capacity() * 2)?在需要進行內存重新分配時,調用reserve函數來預留更多的內存空間。這里使用了三目運算符,如果當前容量為0,則預留4個元素的空間,否則將當前容量乘以2來預留更多的空間。

  4. *_finish = x?將傳入的元素x賦值給_finish指針所指向的位置,即在vector的末尾插入元素。

  5. ++_finish?將_finish指針向后移動一位,指向新插入元素的下一個位置,以便維護vector的邊界。

5、迭代器

typedef T* iterator;
typedef const T* const_iterator;iterator begin()
{return _start;
}iterator end()
{return _finish;
}const_iterator begin() const
{return _start;
}const_iterator end() const
{return _finish;
}
  • 首先,通過typedef關鍵字,定義了兩個迭代器類型:iteratorconst_iteratoriterator表示可修改元素的迭代器,而const_iterator表示只讀元素的迭代器。
  • 然后,定義了begin()end()函數的多個重載版本,用于返回不同類型的迭代器。

6、empty

bool empty()
{return _start == _finish;
}

7、pop_back

void pop_back(const T& x)
{assert(!empty());--_finish;
}

8、operator[ ]

這個類中有兩個重載的下標運算符函數,一個是非常量版本的?operator[],另一個是常量版本的?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];
}

9、resize

void resize(size_t n, T val = T())
{if (n < size()){_finish = _start + n;}else {if (n 》 capacity())reserve(n);while (_finish != _start + n){*_finish = val;++_finish;}}
}

函數簽名為?void resize(size_t n, T val = T()),接受兩個參數:n?表示新的大小,val?表示新元素的默認值(默認為?T(),通過匿名對象T()調用類型?T?的默認構造函數)。

函數的作用是將容器的大小調整為?n。如果?n?小于當前的大小,則將容器的大小縮小為?n,丟棄多余的元素;如果?n?大于當前的大小,則在容器的末尾添加新的元素,直到容器的大小達到?n

  1. 首先,函數會檢查?n?是否小于當前的大小。如果是,說明需要縮小容器的大小,將?_finish?指針移動到新的位置?_start + n,丟棄多余的元素。
  2. 如果?n?大于等于當前的大小,則需要添加新的元素。首先,函數會檢查?n?是否大于容器的容量?capacity()。如果?n?大于容量,則調用?reserve?函數來增加容器的容量,以確保容器有足夠的空間來存放新的元素。
  3. 然后,使用循環將新的元素?val?添加到容器的末尾,直到容器的大小達到?n。循環中,將?val?賦值給?_finish?指向的位置,然后將?_finish?指針向后移動一位。

匿名對象調用默認構造初始化。

    template<class T>void f(){T x = T();cout << x << endl;}

  • 在?resize?函數中,T val = T()?是一個帶有默認值的函數參數。這里?T()?是對模板參數?T?類型的值初始化,對于內置類型,它會初始化為零(對于指針類型,初始化為?nullptr)。這和?f<T>()?模板函數中的?T x = T()?是一樣的。
  • 當你調用?resize?函數時,如果你沒有提供第二個參數,那么?val?就會被初始化為?T?類型的默認值。然后,resize?函數會使用?val?的值來填充新添加的元素。
  • 例如,如果你有一個?byte::vector<int>?對象?v,并調用?v.resize(10),那么?resize?函數會將?v?的大小改變為?10,并使用?int?類型的默認值?0?來填充新添加的元素。這和?f<int>()?函數打印?int?類型的默認值?0?是一樣的。

?內置類型的默認初始化和直接初始化。

	void test_vector2(){// 內置類型有沒有構造函數int i = int();int j = int(1);f<int>();f<int*>();f<double>();}
  • int i = int();?使用值初始化,將?i?初始化為零。int j = int(1);?使用直接初始化,將?j?初始化為?1。

  • 分別使用?int、int* 和?double?作為模板參數調用了?f<T>()?函數。這將分別打印?int、int* 和?double?類型的默認值,即?0、nullptr?和?0。

10、insert

iterator insert(iterator pos, const T& val)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_storage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);// 擴容后更新pos,解決pos失效的問題pos = _start + len;}iterator end = _finish-1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;
}

函數接受兩個參數,第一個參數?pos?是一個迭代器,表示要插入元素的位置,第二個參數?val?是要插入的元素的值。

函數的實現分為以下幾個步驟:

  1. 首先,使用?assert?斷言來確保?pos?是一個有效的位置,即?pos?必須在?_start?和?_finish?之間。

  2. 然后,檢查是否有足夠的空間來插入新的元素。如果?_finish?等于?_end_of_storage,表示當前的內存已經用完,需要重新分配內存。這時,會調用?reserve?函數來重新分配內存,新的容量是當前容量的兩倍,如果當前容量為?0,則新的容量為?4。然后,更新?pos?的值,因為重新分配內存后,原來的?pos?可能已經失效。

  3. 接下來,從?_finish-1?開始,將每個元素向后移動一位,直到?pos?的位置,為插入新的元素騰出空間。

  4. 然后,將?val?的值賦給?*pos,即在?pos?的位置插入新的元素。

  5. 最后,將?_finish?向后移動一位,表示?vector?的大小增加了一個元素。

  6. 函數返回插入新元素的位置?pos

迭代器失效問題

  1. 在 `byte::vector` 類的 `insert` 函數中,如果需要重新分配內存(即 `_finish+ + == _end_of_storage`),那么所有指向原來內存的迭代器都會失效。這是因為 `reserve` 函數會申請新的內存,復制原來的元素到新的內存,然后釋放原來的內存。這個過程會導致原來的內存地址不再有效,因此所有指向原來內存的迭代器都會失效。
  2. 在這個函數中,`pos` 是一個迭代器,它指向要插入新元素的位置。如果在插入新元素之前需要重新分配內存,那么 `pos` 就會失效。為了解決這個問題,函數在重新分配內存后,會根據 `pos` 原來的位置(即 `len = pos - _start`)來更新 `pos` 的值(即 `pos = _start + len`)。這樣,`pos` 就會指向新內存中相同的位置。
  3. 所以,如果你在調用 `insert` 函數之后還需要使用原來的迭代器,你需要注意迭代器可能已經失效。你可以在插入新元素后,重新獲取迭代器的值。例如,如果你在插入新元素后,想要訪問新元素,這里不能常量pos使用引用傳值,你可以使用 `insert` 函數的返回值,它返回的是插入新元素的位置。這時外部插入元素后? (*pos)++; 可以正常運行了。

11、erase

我們先看這個版本的erase:

void erase(iterator pos)
{assert(pos >= _start && pos < _finish);iterator start = pos + 1;while (start != _finish){*(start - 1) = *start;++start;}--_finish;
}

?當我們運行以下代碼程序VS會報錯,linux下g++不會報錯。

	void test4(){std::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1){cout << e << " ";}cout << endl;auto pos = find(v1.begin(), v1.end(), 2);if (pos != v1.end()){v1.erase(pos);}(*pos)++;for (auto e : v1){cout << e << " ";}cout << endl;}
}

VS下:?

g++下:

這段代碼中,v1.erase(pos)?會刪除?vector?中的一個元素,這會導致?pos?以及所有在?pos?之后的迭代器失效。然后,代碼試圖通過?(*pos)++?訪問和修改已經失效的迭代器?pos,這是未定義行為,可能會導致程序崩潰或其他錯誤。

至于為什么 Visual Studio(VS) 會報錯,而 g++ 不會報錯,這主要是因為不同的編譯器對未定義行為的處理方式不同。VS 的調試模式下對迭代器進行了更嚴格的檢查,當你試圖訪問失效的迭代器時,它會立即報錯。而 g++ 在默認設置下可能不會進行這樣的檢查,所以它可能不會立即報錯,但這并不意味著這段代碼是正確的。

下面第一種情況刪除非末尾元素時,VS的報錯沒有意義,但在第二種情況下,VS的報錯就非常有意義了。?

為了避免這種問題,你應該在刪除元素后,不再使用已經失效的迭代器。如果你需要在刪除元素后繼續訪問?vector,你應該在刪除元素后重新獲取迭代器的值。例如,vector::erase?函數會返回一個指向被刪除元素之后的元素的迭代器,你可以使用這個返回值來更新?pos?。

正確版本:?

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

我們來測試一下刪除偶數:

void test5()
{byte::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1){cout << e << " ";}cout << endl;//要求刪除所有偶數byte::vector<int>::iterator it = v1.begin();while (it != v1.end()){if (*it % 2 == 0){it=v1.erase(it);}else{++it;}}for (auto e : v1){cout << e << " ";}cout << endl;
}

?

12、帶參初始化

?一定要對_start、_finish、_out_of_storage進行初始化,不初始化默認隨機值。?

vector(size_t n, const T& value = T()): _start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{reserve(n);while (n--){push_back(value);}
}

這個構造函數創建一個包含?n?個元素的?vector,每個元素都初始化為?valuevalue?參數有一個默認值,即?T(),它是?T?類型的默認構造值。

  • _start(nullptr), _finish(nullptr), _end_of_storage(nullptr): 這一行初始化三個迭代器,它們分別指向數組的開始、當前最后一個元素之后的位置,和分配的內存末端。初始化為?nullptr?表示開始時沒有分配任何內存。
  • reserve(n): 這個函數調用會分配足夠容納?n?個元素的內存,但不會創建任何元素。
  • while (n--) { push_back(value); }: 這個循環會不斷地添加?value?到?vector?中,直到添加了?n?個元素。push_back?函數會在?vector?的末尾添加一個新元素,并可能會增加?vector?的容量(如果需要)。

為什么對 T& 前面要加 const ?

  • 匿名對象聲明周期只在當前一行,因為這行之后沒人會用它了。
  • const引用會延長匿名對象的聲明周期到引用對象域結束,因為以后用xx就是用匿名對象。

13、迭代器初始化

template <class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}

這個構造函數使用兩個迭代器?first?和?last,它們分別指向輸入序列的開始和結束,來初始化?vector。這個構造函數可以用于從任何可迭代的容器(如另一個?vector、列表、數組等)復制元素。

  • 在這個構造函數中,沒有顯式地調用?reserve?來預分配內存。這意味著每次用?push_back?時,如果當前容量不足以容納新元素,就會自動進行內存重新分配。
  • while (first != last) { push_back(*first); ++first; }: 這個循環會遍歷輸入序列的每個元素,從 first 開始,一直到達 last(但不包括 last),并使用每個元素的值調用 push_back,將其添加到 vector 中。

?但是對于這句代碼編譯之后會報錯:

vector<int> v1(10, 5);

?這是因為這段代碼在vector(InputIterator first, InputIterator last)和vector(size_t n, const T& value = T())同時存在時,會優先調用前者,但調研之后在函數內部first的模板類型為int,而*first為對int類型解引用,所以這樣報錯了。

我們只要添加一個int類型重載函數即可解決。

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& value = T())。

vector<int> v1(10u, 5);

14、析構函數

~vector()
{delete[] _start;_start = _finish = _end_of_storage = nullptr;
}

完整版代碼&測試代碼

#pragma once
#include<assert.h>
namespace byte
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}void resize(size_t n, T val = T()){if (n < size()){_finish = _start + n;}else {if (n < capacity())reserve(n);while (_finish != _start + n){*_finish = val;++_finish;}}}vector():_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){}vector(size_t n, const T& value = T()): _start(nullptr), _finish(nullptr), _end_of_storage(nullptr){reserve(n);while (n--){push_back(value);}}vector(int n, const T& val = T()){reserve(n);for (int i = 0; i < n; ++i){push_back(val);}}template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}~vector(){delete[] _start;_start = _finish = _end_of_storage = nullptr;}void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){memcpy(tmp, _start, sizeof(T) * size());delete[] _start;}_start = tmp;_finish = _start + sz;_end_of_storage = _start + n;}}void push_back(const T& x){if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;}void pop_back(const T& x){assert(!empty());--_finish;}void insert(iterator pos, const T& val){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 = val;++_finish;}iterator erase(iterator pos){assert(pos >= _start && pos < _finish);iterator start = pos + 1;while (start != _finish){*(start - 1) = *start;++start;}--_finish;return pos;}size_t capacity() const{return _end_of_storage - _start;}size_t size() const{return _finish - _start;}bool empty(){return _start == _finish;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}private:iterator _start;iterator _finish;iterator _end_of_storage;};void func(const vector<int>& v){for (size_t i = 0; i < v.size(); ++i){cout << v[i] << " ";}cout << endl;vector<int>::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl << endl;}void test1(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);for (size_t i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;vector<int>::iterator it = v1.begin();while (it != v1.end()){cout << *it << " ";++it;}cout << endl;for (auto e : v1){cout << e << " ";}cout << endl;}void test2(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);cout << v1.size() << endl;cout << v1.capacity() << endl;v1.resize(10);cout << v1.size() << endl;cout << v1.capacity() << endl;func(v1);v1.resize(3);func(v1);}void test3(){std::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);//v1.push_back(5);for (auto e : v1){cout << e << " ";}cout << endl;/*v1.insert(v1.begin(), 0);for (auto e : v1){cout << e << " ";}cout << endl;*/auto pos = find(v1.begin(), v1.end(), 3);if (pos != v1.end()){//v1.insert(pos, 30);pos = v1.insert(pos, 30);}for (auto e : v1){cout << e << " ";}cout << endl;// insert以后我們認為pos失效了,不能再使用(*pos)++;for (auto e : v1){cout << e << " ";}cout << endl;}void test4(){std::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1){cout << e << " ";}cout << endl;//auto pos = find(v1.begin(), v1.end(), 2);auto pos = find(v1.begin(), v1.end(), 4);if (pos != v1.end()){v1.erase(pos);}(*pos)++;for (auto e : v1){cout << e << " ";}cout << endl;}void test5(){byte::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1){cout << e << " ";}cout << endl;//要求刪除所有偶數byte::vector<int>::iterator it = v1.begin();while (it != v1.end()){if (*it % 2 == 0){it=v1.erase(it);}else{++it;}}for (auto e : v1){cout << e << " ";}cout << endl;}void test6(){vector<int> v1(10, 5);for (auto e : v1){cout << e << " ";}cout << endl;vector<int> v2(v1.begin() + 1, v1.end() - 1);for (auto e : v2){cout << e << " ";}cout << endl;std::string s1("hello");vector<int> v3(s1.begin(), s1.end());for (auto e : v3){cout << e << " ";}cout << endl;int a[] = { 100, 10, 2, 20, 30 };vector<int> v4(a, a + 3);for (auto e : v4){cout << e << " ";}cout << endl;v1.insert(v1.begin(), 10);for (auto e : v1){cout << e << " ";}cout << endl;}
}

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

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

相關文章

一款基于ESP32的迷你四足機器人

一、軟件介紹 增加自定義動作模式&#xff0c;可以在小程序中自定義一個最多10個步驟的動作。 附件中&#xff1a;帶自定模式固件bin.zip esp32c3固件文件 燒錄下圖設置 無串口版本esp32c3開發板燒錄前先按住BOOT鍵再插線進入燒錄模式&#xff0c;LoadMode選擇USB。 二、AP…

2023團體程序設計天梯賽——模擬賽和總決賽題

M-L1-1 嫑廢話上代碼 Linux 之父 Linus Torvalds 的名言是&#xff1a;“Talk is cheap. Show me the code.”&#xff08;嫑廢話&#xff0c;上代碼&#xff09;。本題就請你直接在屏幕上輸出這句話。 輸入格式&#xff1a; 本題沒有輸入。 輸出格式&#xff1a; 在一行中輸出…

java resource ‘process/qingjia.png‘ not found

resource中的資源在target中沒有&#xff0c;導致報錯&#xff0c;如下圖所示&#xff1a; 解決辦法&#xff1a;在pom文件中添加如下代碼&#xff1a; 重新執行代碼&#xff0c;就能在target中看到png文件了。 類似的錯誤參考鏈接&#xff1a;mybatis-plus框架報錯&#x…

STL模板參數類字段名稱類型參數模板解析方法

指向成員的指針允許您引用類對象的非靜態成員。不能使用指向成員的指針指向靜態類成員&#xff0c;因為靜態成員的地址不與任何特定對象相關聯。若要指向靜態類成員&#xff0c;必須使用普通指針。可以使用指向成員函數的指針&#xff0c;其方式與指向函數的指針相同。您可以比…

【C/C++】可變參數va_list與格式化輸出

va_list與格式化輸出 va_list 文章目錄 va_list與格式化輸出va_list格式化輸出snprintfvsnprintfvasprintf 實例 va_list是在C語言中解決變參問題的一組宏&#xff0c;變參問題是指參數的個數不定&#xff0c;可以是傳入一個參數也可以是多個 用法&#xff1a;在函數里定義va_…

Java 手寫設計HashMap源碼,讓面試官膜拜

Java 手寫HashMap源碼&#xff0c;讓面試官膜拜 一&#xff0c;手寫源碼 這是一個模仿HashMap的put&#xff0c;get功能的自定義的MyHashMap package cn.wxs.demo;import java.io.Serializable; import java.util.*; import java.util.function.BiConsumer;class MyHashMap&…

面向對象三大特征——封裝

目錄 1. 封裝概述&#xff08;封裝與隱藏&#xff09; 2. private關鍵字 3. Getter & Setter方法 4. 變量訪問原則和this關鍵字 5. 構造方法 5.1 構造方法概述 5.2 構造方法和set方法的比較 6. 靜態 6.1 靜態概述 6.2 靜態效果 6.3 靜態變量和非靜態變量的區別 …

win11 CUDA(12.3) + cuDNN(12.x) 卸載

win11 CUDA&#xff08;12.3&#xff09; cuDNN&#xff08;12.x&#xff09;卸載 信息介紹卸載 信息介紹 本文是對應 win11RTX4070Ti 安裝 CUDA cuDNN&#xff08;圖文教程&#xff09; 的卸載 卸載 控制面板 --> 程序 --> 卸載程序 卸載掉圖中紅框內的&#xff0c…

C語言-水仙花數

水仙花數是指一個N位正整數&#xff08;N≥3&#xff09;&#xff0c;它的每個位上的數字的N次冪之和等于它本身。例如&#xff1a;153135333。 本題要求編寫程序,計算所有N位水仙花數。 輸入格式: 輸入在一行中給出一個正整數N&#xff08;3≤N≤7&#xff09;。 輸出格式…

reinforce 跑 CartPole-v1

gym版本是0.26.1 CartPole-v1的詳細信息&#xff0c;點鏈接里看就行了。 修改了下動手深度強化學習對應的代碼。 然后這里 J ( θ ) J(\theta) J(θ)梯度上升更新的公式是用的不嚴謹的&#xff0c;這個和王樹森書里講的嚴謹公式有點區別。 代碼 import gym import torch from …

innobackupex備份目錄

innobackupeex全備腳本思路 四個需求如下&#xff1a; &#xff08;1&#xff09;每天晚上23點執行&#xff0c;這需要linux系統做一個定時任務 00 23 * * * /bin/sh /shell/tencent_xtrabackup_all.sh /dev/null 2>&1 &#xff08;2&#xff09;每天。。看到這個詞…

標識符···

定義 標識符只能由字母、數字、下劃線&#xff08;_&#xff09;和美元符號&#xff08;$&#xff09;組成。標識符必須以字母、下劃線或美元符號開頭&#xff0c;不能以數字開頭。標識符對大小寫敏感&#xff0c;例如"myVariable"和"myvariable"是不同的…

Android 11 適配——整理總結篇

背景 > 經過檢測&#xff0c;我們識別到您的應用&#xff0c;目前未適配安卓11&#xff08;API30&#xff09;&#xff0c;請您關注適配截止時間&#xff0c;盡快開展適配工作&#xff0c;避免影響應用正常發布和經營。 > targetSdkVersion30 升級適配工作參考文檔&am…

從零開發短視頻電商 Jmeter壓測示例模板詳解(無認證場景)

文章目錄 添加線程組添加定時器添加HTTP請求默認值添加HTTP頭管理添加HTTP請求添加結果斷言響應斷言 Response AssertionJSON斷言 JSON Assertion持續時間斷言 Duration Assertion 添加察看結果樹添加聚合報告添加表格察看結果參考 以壓測百度搜索為例 https://www.baidu.com/s…

class066 一維動態規劃【算法】

class066 一維動態規劃 算法講解066【必備】從遞歸入手一維動態規劃 code1 509斐波那契數列 // 斐波那契數 // 斐波那契數 &#xff08;通常用 F(n) 表示&#xff09;形成的序列稱為 斐波那契數列 // 該數列由 0 和 1 開始&#xff0c;后面的每一項數字都是前面兩項數字的和。…

kotlin - ViewBinding

前言 為什么用ViewBinding&#xff0c;而不用findViewById()&#xff0c;這個有很多優秀的博主都做了講解&#xff0c;就不再列出了。 可參考下列博主的文章&#xff1a; kotlin ViewBinding的使用 文章里也給出了如何在gradle中做出相應的配置。 &#xff08;我建議先看這位博…

【LeetCode熱題100】【滑動窗口】無重復字符的最長子串

給定一個字符串 s &#xff0c;請你找出其中不含有重復字符的 最長子串 的長度。 示例 1: 輸入: s "abcabcbb" 輸出: 3 解釋: 因為無重復字符的最長子串是 "abc"&#xff0c;所以其長度為 3。示例 2: 輸入: s "bbbbb" 輸出: 1 解釋: 因為無…

Docker安裝教程

docker官網 1.卸載舊版 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine2.配置Docker的yum庫 安裝yum工具 yum install -y yum-utils配置Docker的yum源 yum-config-ma…

Redis,什么是緩存穿透?怎么解決?

Redis&#xff0c;什么是緩存穿透&#xff1f;怎么解決&#xff1f; 1、緩存穿透 一般的緩存系統&#xff0c;都是按照key去緩存查詢&#xff0c;如果不存在對用的value&#xff0c;就應該去后端系統查找&#xff08;比如DB數據庫&#xff09;。一些惡意的請求會故意查詢不存在…

不想寫大量 if 判斷?試試用規則執行器優化,就很絲滑!

近日在公司領到一個小需求&#xff0c;需要對之前已有的試用用戶申請規則進行拓展。我們的場景大概如下所示: if (是否海外用戶) {return false; }if (刷單用戶) {return false; }if (未付費用戶 && 不再服務時段) {return false }if (轉介紹用戶 || 付費用戶 || 內推…