智能指針(Newbie Note)

智能指針專題

  • 1.普通指針的問題
  • 2.智能指針是什么
    • 什么是所有權
  • 3.智能指針三個好處:
  • 4.C++11提供的智能指針
    • 4.1 shared_ptr(共享所有權指針)
      • 4.1.1 分配內存
      • 4.1.2 成員函數
      • 4.1.3 計數情況匯總:
      • 4.1.4 示例代碼(計數)
      • 4.1.5 示例代碼(reset)
      • 4.1.6 自定義刪除器
      • 4.1.7 shared_ptr的陷阱
    • 4.2 unique_ptr(獨占所有權指針)
      • 4.2.1 分配內存
      • 4.2.2 成員函數
      • 4.2.3 拷貝賦值
      • 4.2.4 release
      • 4.2.5 reset
      • 4.2.6 自定義刪除器
      • 4.2.7 unique_ptr的陷阱
        • 不要與裸指針混用
  • 5.性能
  • 6.選擇指針
    • 6.1 unique_ptr場景示例:
      • 6.1.1 作為參數
      • 6.1.2 作為返回值
    • 6.2 shared_ptr場景示例:
        • 6.2.1 作為參數
  • 參考資料

1.普通指針的問題

  • 1.訪問失敗:如果一塊內存被多個指針引用,但其中的一個指針釋放且其余的指針并不知道,這樣的情況下,就發生了訪問失敗。

  • 2.內存泄漏:從堆中申請了內存后不釋放回去,就會引發內存泄漏。

2.智能指針是什么

在構造的時候分配內存,當離開作用域的時候,自動釋放分配的內存,這樣的話開發人員就可以從手動動態管理內存的繁雜內容中解放出來。

每種智能指針都是以類模板的方式實現的,shared_ptr 也不例外。

什么是所有權

https://blog.csdn.net/weixin_39722329/article/details/96301534

  • 單一所有權:所有權擁有著有義務去釋放或轉移所有權,同一時刻只會有一個所有權擁有者

  • 共享所有權:和使用裸指針一樣,所有權的使用者不必釋放內存,引用計數器會負責釋放內存,同一時刻可以有多個所有權擁有者。

3.智能指針三個好處:

  • 1.明確資源的所屬權

  • 2.避免忘記delete,這種比較容易犯錯誤

  • 3.更好的處理異常

//函數結束后shared_ptr自動釋放內存
void f(){shared_ptr<int> sp(new int(11));//假設拋出了異常,而且在f中未捕獲
}//函數結束后ip所指向的內存沒有被釋放。
void f1(){int* ip = new int(12);//假設delete語句前拋出了異常,而且在f中未捕獲delete ip;
}

重要性:1>>2>3

4.C++11提供的智能指針

4.1 shared_ptr(共享所有權指針)

??共享指針shared_ptr是具有共享所有權語義的智能指針。 每當共享指針shared_ptr的最后一個所有者被銷毀時,關聯對象都將被刪除(或關聯資源被清除)。

4.1.1 分配內存

? std::make_shared

// make_shared<int>分配一塊int類型大小的內存,并值初始化為100(返回值是shared_ptr類型,因此可以直接賦值給sp)
shared_ptr<int> sp = std::make_shared<int>(100);

? new 接受指針參數的智能指針構造函數是explicit的,直接初始化形式

// 錯誤! 不會進行隱式轉換,類型不符合
shared_ptr<int> sp1 = new int(100);
// 正確,直接初始化調用構造函數
shared_ptr<int> sp2(new int(100000));

4.1.2 成員函數

  • p.get()
    p.get()的返回值就相當于一個裸指針的值,使用遵守以下幾個約定
    1.不要保存p.get()的返回值
    2.無論是保存為裸指針還是shared_ptr都是錯誤的
    3.保存為裸指針不知什么時候就會變成空懸指針
    4.保存為shared_ptr則產生了獨立指針
    5.不要delete p.get()的返回值
    6.會導致對一塊內存delete兩次的錯誤
  • swap(p,q)
    ? 交換p、q中保存的指針
  • shared_ptr<T> p(q)
    ? p是q的拷貝,它們指向同一塊內存,互相關聯
  • p = q
    ? 用q為p賦值,之后p、q指向同一塊內存,q引用計數+1,p(原來內存空間的)引用計數-1
  • p.use_count()
    ? 返回與p共享對象的智能指針數量
  • shared_ptr<T> p(q,d)
    ? q是一個可以轉換為T*的指針,d是一個可調用對象(作為刪除器),p接管q所指對象的所有權,用刪除器d代替delete釋放內存
  • p.reset()
    ? 將p重置為空指針
  • p.reset(p)
    ? 將p重置為p(的值)
  • p.reset(p,d)
    ? 將p重置為p(的值)并使用d作為刪除器
  • shared_ptr為什么沒有release()
    ? 對于shared_ptr,是可能存在多個shared_ptr指向同一塊內存,如果提供了release可能會造成錯誤的釋放,導致其他shared_ptr出現錯誤。

4.1.3 計數情況匯總:

賦值(增加)

auto sp = make_shared<int>(1024); // sp的引用計數為1
#include <iostream>
#include <memory>using namespace std;
// compile:g++ test.cpp -o a.exe -std=c++11int main()
{{auto sp1 = make_shared<string>("obj1");auto sp2(sp1);auto sp3 = make_shared<string>("obj2");cout << "before sp2->use_count() = " << sp2.use_count() << '\n';cout << "before sp3->use_count() = " << sp3.use_count() << '\n';sp1 = sp3; // 該操作會減少sp2的引用計數,增加sp3的引用計數cout << "after sp2->use_count() = " << sp2.use_count() << '\n';cout << "after sp3->use_count() = " << sp3.use_count() << '\n';}return 0;
}

該操作會減少sp2的引用計數,增加sp3的引用計數。(sp1、sp2指向對象obj1,sp3指向對象obj2,那么賦值之后,sp1也會指向obj2,那就是說指向obj1的就少了,指向obj2的就會多。)

拷貝(增加)

auto sp2 = make_shared<int>(1024);
auto sp1(sp2);

該操作會使得sp1和sp2都指向同一個對象。

傳參(拷貝)(增加)
而關于拷貝比較容易忽略的就是作為參數傳入函數:

auto sp2 = make_shared<int>(1024);
func(sp2); // func的執行會增加其引用計數

reset(減少)

釋放sp指向的對象( 而如果sp是唯一指向該對象的,則該對象被銷毀 )

sp.reset()

4.1.4 示例代碼(計數)

參考:https://en.cppreference.com/w/cpp/memory/shared_ptr

#include <iostream>
#include <memory>// g++ test.cpp -o a.exe -std=c++11 class Base
{
public:Base() { std::cout << "  Base::Base()\n"; }~Base() { std::cout << "  Base::~Base()\n"; }
};class Derived: public Base
{
public:Derived() { std::cout << "  Derived::Derived()\n"; }~Derived() { std::cout << "  Derived::~Derived()\n"; }
};void test(std::shared_ptr<Base> p)增加引用計數
{   std::cout << "local pointer in a function:\n"<< "  p.get() = " << p.get()<< ", p.use_count() = " << p.use_count() << '\n';std::shared_ptr<Base> lp = p; std::cout << "local pointer in a function:\n"<< "  lp.get() = " << lp.get()<< ", lp.use_count() = " << lp.use_count() << '\n';
}//銷毀ptr,減少引用計數int main()
{std::shared_ptr<Base> p = std::make_shared<Derived>();std::cout << "func before:\n"<< "  p.get() = " << p.get()<< ", p.use_count() = " << p.use_count() << '\n';test(p);std::cout << "func after:\n"<< "  p.get() = " << p.get()<< ", p.use_count() = " << p.use_count() << '\n';
}

運行結果

Base::Base()
Derived::Derived()
func before:p.get() = 0x1028c30, p.use_count() = 1
local pointer in a function:p.get() = 0x1028c30, p.use_count() = 2
local pointer in a function:lp.get() = 0x1028c30, lp.use_count() = 3
func after:p.get() = 0x1028c30, p.use_count() = 1
Derived::~Derived()
Base::~Base()

4.1.5 示例代碼(reset)

重置共享指針,減少計數

#include<memory>
#include<iostream>
// 編譯: g++ test.cpp -o a.exe -std=c++11using namespace std;
class A {
public:int i ;A() { cout << "construct\n"; }~A() { cout << "delete "<< i <<"\n"; }
};
int main()
{// 共享指針a,b,c都指向堆內存new A的位置shared_ptr<A> a( new A);shared_ptr<A> b(a);shared_ptr<A> c(b);shared_ptr<A> d(new A);a->i = 10;cout << a.use_count() << endl;cout << b.use_count() << endl;d->i = 30;// 錯:不要用p.get()的返回值為shared_ptr賦值,因為返回的是裸指針,很容易被共享指針重復釋放,造成錯誤//a.reset(d.get())// 令a釋放指向的空間 A//a.reset();// 令a釋放指向的空間 A,指向新空間a.reset(new A);a->i = 20;cout << b.use_count() << endl;cout << "end" <<endl;return 0;
}

4.1.6 自定義刪除器

??如果用shared_ptr管理非new對象或是沒有析構函數的類時,應當為其傳遞合適的刪除器,原理是:當刪除器的指針Deleter傳給shared_ptr/unique_ptr時,shared_ptr/unique_ptr不會使用默認的delete val來釋放其管理的資源,而是使用Deleter(val)來釋放資源,這樣就調用了Deleter來釋放管理的資源。

1.普通刪除函數定義類似于:

void Deleter(T *val){// 其他代碼// 釋放val的內存delete val;// 或者(如果val是數組)delete[] val;
}
#include <iostream>
#include <memory>
#include <string>
// 編譯: g++ test.cpp -o a.exe -std=c++11
using namespace std;class Connection{
public:string _name;explicit Connection(string name):_name(name){}string get_name() const {return _name;}
};void close(Connection* connection){cout << string("關閉") + connection->get_name() + "管理的連接中..." << endl;// 關閉連接的代碼// .....cout << "關閉完成。" << endl;
}// 函數式刪除器
void Deleter(Connection *connection){close(connection);delete connection;
}int main(){// 新建管理連接Connection的智能指針shared_ptr<Connection> sp(new Connection("shared_ptr"), Deleter);sp->_name = "hello";
}
  1. 自定義釋放規則,用于申請的動態數組

??對于申請的動態數組來說,shared_ptr 指針默認的釋放規則是不支持釋放數組的,只能自定義對應的釋放規則,才能正確地釋放申請的堆內存。釋放規則可以使用 C++11 標準中提供的 default_delete 模板類,我們也可以自定義釋放規則:

//1.指定 default_delete 作為釋放規則
std::shared_ptr<int> p6(new int[10], std::default_delete<int[]>());//2.自定義釋放規則
void deleteInt(int*p) {delete []p;
}
//初始化智能指針,并自定義釋放規則
std::shared_ptr<int> p7(new int[10], deleteInt);//3.lambda方式構造和釋放
std::shared_ptr<int> p7(new int[10], [](int* p) {delete[]p; });  

4.1.7 shared_ptr的陷阱

1.不要與裸指針混用
錯誤場景1:

int *x(new int(10));
shared_ptr<int> sp1(x);
shared_ptr<int> sp2(x);//x隨時可能變成空懸指針而無從知曉

錯誤場景2:

int *x(new int(10));
//創建了一個指向x指針所指內存的共享指針,引用計數為1,是引用這塊內存的唯一共享指針func(shared_ptr<int> (x));
//離開函數即離開共享指針的作用域,這塊內存即被刪除

2.謹慎使用p.get()的返回值

shared_ptr<int> sp1(new int(10));
shared_ptr<int> sp2(sp1), sp3;
sp3 = sp1;//一個典型的錯誤用法
shared_ptr<int> sp4(sp1.get()); 
cout << sp1.use_count() << " " << sp2.use_count() << " " << sp3.use_count() << " " << sp4.use_count() << endl;//輸出:
3  
3 
3  
1(獨立)

sp1,sp2,sp3是相互關聯的共享指針,共同控制所指內存的生存期,sp4雖然指向同樣的內存,卻是與sp1,sp2,sp3獨立的,sp4按自己的引用計數來關聯內存的釋放。

4.2 unique_ptr(獨占所有權指針)

兩個unique_ptr不能指向同一個對象,不能進行復制操作,只能進行移動操作。

4.2.1 分配內存

? 與shared_ptr不同,unique_ptr沒有定義類似make_shared的操作,因此只可以使用new來分配內存,并且由于unique_ptr不可拷貝和賦值,初始化unique_ptr必須使用直接初始化的方式。

unique_ptr<int> up1(new int());    // okay:直接初始化std::unique_ptr<int> p4(new int);
std::unique_ptr<int> p5(std::move(p4)); // okay:調用移動構造函數,p5 將獲取 p4 所指堆空間的所有權,而 p4 將變成空指針(nullptr)unique_ptr<int> up2 = new int();   // error! 避免隱式轉換
unique_ptr<int> up3(up1);          // error! 不允許拷貝

4.2.2 成員函數

  • up.release()

    ? up放棄對它所指對象的控制權,并返回保存的指針,將up置為空,不會釋放內存

  • up.reset()
    參數可以為空,內置指針,先將up所指對象釋放,然后重置up的值

4.2.3 拷貝賦值

??前面說了unique_ptr不可拷貝和賦值,那要怎樣傳遞unique_ptr參數和返回unique_ptr呢? 事實上不能拷貝unique_ptr的規則有一個例外:我們可以拷貝或賦值一個將要被銷毀的unique_ptr

// 從函數返回一個unique_ptr
unique_ptr<int> func1(int a)
{return unique_ptr<int> (new int(a));
}// 返回一個局部對象的拷貝
unique_ptr<int> func2(int a)
{unique_ptr<int> up(new int(a));return up;
}

或者是引用

void func1(unique_ptr<int> &up){cout<<*up<<endl;
}unique_ptr<int> func2(unique_ptr<int> up){cout<<*up<<endl;return up;
}// 使用up作為參數
unique_ptr<int> up(new int(10));// 傳引用,不拷貝,不涉及所有權的轉移
func1(up);// 暫時轉移所有權,函數結束時返回拷貝,重新收回所有權
up = func2(unique_ptr<int> (up.release()));
// 如果不用up重新接受func2的返回值,這塊內存就泄漏了

4.2.4 release

釋放方法:注意!注意!注意!這里的釋放并不會摧毀其指向的對象,而且將其指向的對象釋放出去。

#include <iostream>
#include <memory>//編譯:g++ test.cpp -o a.exe -std=c++11int main () {std::unique_ptr<int> auto_pointer(new int);int * manual_pointer;*auto_pointer=10;manual_pointer = auto_pointer.release();// (auto_pointer is now empty)std::cout << "manual_pointer points to " << *manual_pointer << '\n';delete manual_pointer;return 0;
}

執行結果為:

manual_pointer points to 10

4.2.5 reset

??重置方法,銷毀由該智能指針管理的任何可能存在的對象,該智能指針被指為空

#include <iostream>
#include <memory>//編譯:g++ test.cpp -o a.exe -std=c++11int main () {std::unique_ptr<int> up;  // emptyup.reset (new int);       // takes ownership of pointer*up=5;std::cout << *up << '\n';up.reset (new int);       // deletes managed object, acquires new pointer*up=10;std::cout << *up << '\n';up.reset();               // deletes managed objectreturn 0;
}

執行結果:

5
10

4.2.6 自定義刪除器

#include <iostream>
#include <memory>
#include <string>
// 編譯: g++ test.cpp -o a.exe -std=c++11
using namespace std;class Connection{
public:string _name;explicit Connection(string name):_name(name){}string get_name() const {return _name;}
};void close(Connection* connection){cout << string("關閉") + connection->get_name() + "管理的連接中..." << endl;// 關閉連接的代碼// .....cout << "關閉完成。" << endl;
}// 函數式刪除器
void Deleter(Connection *connection){close(connection);delete connection;
}int main(){// 新建管理連接Connection的智能指針unique_ptr<Connection, decltype(Deleter)*> up(new Connection("unique_ptr"), Deleter);up->_name = "hello";
}

4.2.7 unique_ptr的陷阱

不要與裸指針混用

錯誤做法

int *x(new int());
unique_ptr<int> up1,up2;
// 會使up1 up2指向同一個內存
up1.reset(x);
up2.reset(x);

??unique_ptr不允許兩個獨占指針指向同一個對象,在沒有裸指針的情況下,我們只能用release獲取內存的地址,同時放棄對對象的所有權,這樣就有效避免了多個獨占指針同時指向一個對象。
正確做法

unique_ptr<int> up1(new int());    // okay,直接初始化
unique_ptr<int> up2;
up2.reset(up1.release());

5.性能

  1. 內存占用高
    shared_ptr 的內存占用是裸指針的兩倍。因為除了要管理一個裸指針外,還要維護一個引用計數。
    因此相比于 unique_ptr, shared_ptr 的內存占用更高
  2. 原子操作性能低
    考慮到線程安全問題,引用計數的增減必須是原子操作。而原子操作一般情況下都比非原子操作慢。
  3. 使用移動優化性能
    shared_ptr 在性能上固然是低于 unique_ptr。而通常情況,我們也可以盡量避免 shared_ptr 復制。
    如果,一個 shared_ptr 需要將所有權共享給另外一個新的 shared_ptr,而我們確定在之后的代碼中都不再使用這個 shared_ptr,那么這是一個非常鮮明的移動語義。對于此種場景,我們盡量使用 std::move,將 shared_ptr 轉移給新的對象。因為移動不用增加引用計數,性能比復制更好。

6.選擇指針

? 不是說任何地方都要使用智能指針,比如說你想傳遞一個對象到一個函數里,那你就可以使用引用或者普通指針(raw ptr), 這里的引用和普通指針體現的是沒有所屬權(ownership),也就是說函數本身不負責這個對象的生命周期。只有需要體現所屬權(ownership)的創立和變動的時候,采用智能指針。參考

選擇條件:
在使用智能指針的時候,優先選用unique_ptr,原因如下:
1.語義簡單,即當你不確定使用的指針是不是被分享所有權的時候,默認選unique_ptr獨占式所有權,當確定要被分享的時候可以轉換成shared_ptr;
2.unique_ptr效率比shared_ptr高,不需要維護引用計數和背后的控制塊;
3.unique_ptr用起來更順暢,選擇性更多,可以轉換成shared_ptr和通過get和release定制化智能指針(custom smart pointer)。

如果有多個指針指向同一對象的話,你應該使用shared_ptr;

如果一個對象只需要一個智能指針,那你應該是用unique_ptr,它非常適合于返回值類型為unique_ptr的函數

6.1 unique_ptr場景示例:

6.1.1 作為參數

因為不能被拷貝,所以傳遞裸指針或者引用或者外部不需要了直接轉移,但是要注意不能傳遞值(拷貝)

// 裸指針
#include<iostream>
#include<memory>
void test(int *p)
{*p = 10;
}
int main()
{std::unique_ptr<int> up(new int(42));test(up.get());//傳入裸指針作為參數std::cout<<*up<<std::endl;//輸出10return 0;
}// 引用
#include<iostream>
#include<memory>
void test(std::unique_ptr<int> &p)
{*p = 10;
}
int main()
{std::unique_ptr<int> up(new int(42));test(up);std::cout<<*up<<std::endl;//輸出10return 0;
}// 轉移
#include<iostream>
#include<memory>
void test(std::unique_ptr<int> p)
{*p = 10;
}
int main()
{std::unique_ptr<int> up(new int(42));test(std::unique_ptr<int>(up.release()));//test(std::move(up));//這種方式也可以return 0;
}

6.1.2 作為返回值

返回可以用unique_ptr(偽代碼),這里可以理解為返回值是拷貝了指向的地方,相當于std::move,獲取唯一的所有權

unique_ptr<int> make_init(int n)
{return unique_ptr<int> (new int(n));
}int main()
{
···vector<unique_ptr<int>> vp(size);for(int i = 0; i < vp.size(); i++)vp[i] = make_init(rand() % 1000)
···
}

6.2 shared_ptr場景示例:

6.2.1 作為參數
#include<iostream>
#include<memory>
void func0(std::shared_ptr<int> sp)
{std::cout<<"fun0:"<<sp.use_count()<<std::endl;
}void func1(std::shared_ptr<int> sp)
{std::cout<<"fun1:"<<sp.use_count()<<std::endl;
}void func2(std::shared_ptr<int> &sp)
{std::cout<<"fun1:"<<sp.use_count()<<std::endl;
}int main()
{auto sp = std::make_shared<int>(1024);func0(sp);   // 拷貝方式(這種方式unique不可以)func1(sp);	 // 拷貝方式(這種方式unique不可以)func2(sp);   // 引用方式return 0;
}

這里建議傳參使用引用,免拷貝。

參考資料

主要參考:https://www.yanbinghu.com/categories/Cpp/

C++11智能指針: https://www.jianshu.com/p/e4919f1c3a28

C++11 shared_ptr智能指針:http://c.biancheng.net/view/7898.html

shared_ptr官方講解:http://www.cplusplus.com/reference/memory/shared_ptr/shared_ptr/

unique_ptr官方講解:http://www.cplusplus.com/reference/memory/unique_ptr/unique_ptr/

智能指針的選擇:https://blog.csdn.net/qq_22533607/article/details/82318595

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

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

相關文章

Java深拷貝與淺拷貝技術解析及實例演示

摘要&#xff1a;本文將詳細介紹Java中的深拷貝和淺拷貝概念&#xff0c;通過分析源碼和舉例說明&#xff0c;幫助讀者更好地理解這兩種拷貝方式的區別及應用場景。 一、深拷貝與淺拷貝的概念 深拷貝&#xff1a;復制一個對象后&#xff0c;無論是基本數據類型還是引用類型&…

多柱漢諾塔問題

k柱漢諾塔 題目描述 漢諾塔&#xff08;Hanoi Tower&#xff09;&#xff0c;又稱河內塔。 傳說大梵天創造世界的時候做了三根金剛石柱子&#xff0c;按左、中、右排序。大梵天在左側的柱子上&#xff0c;從下往上按照大小順序摞著64片黃金圓盤&#xff0c;越靠下的圓盤越大。…

個人博客項目 - 測試報告

文章目錄 一、項目背景二、測試報告功能測試1.編寫測試用例2.登錄測試3.編寫文章測試4.查看文章測試5.刪除文章測試7.注銷登錄測試 自動化測試性能測試1.VUG2.進行場景設計3.生成性能測試報告 總結 本文開始 一、項目背景 通過學習測試相關的知識&#xff0c;動手實踐并測試一…

2023 年 亞太賽 APMCM ABC題 國際大學生數學建模挑戰賽 |數學建模完整代碼+建模過程全解全析

當大家面臨著復雜的數學建模問題時&#xff0c;你是否曾經感到茫然無措&#xff1f;作為2022年美國大學生數學建模比賽的O獎得主&#xff0c;我為大家提供了一套優秀的解題思路&#xff0c;讓你輕松應對各種難題。 以五一杯 A題為例子&#xff0c;以下是咱們做的一些想法呀&am…

【Vue】自定義指令

自定義指令 自定義指令就是自己定義的指令&#xff0c;是對 DOM 元素進行底層操作封裝 ,程序化地控制 DOM&#xff0c;拓展額外的功能 全局定義 Vue.directive(指令名字, definition) 指令名&#xff1a;不包括v-前綴&#xff0c;使用時候包括v-&#xff0c;v-指令名defini…

CUTLASS 1.3.3中的 Volta884_h884gemm

CUTLASS 是 CUDA C 模板抽象的集合&#xff0c;用于在 CUDA 內的所有級別和規模上實現高性能矩陣-矩陣乘法 (GEMM) 和相關計算。它采用了類似于 cuBLAS 和 cuDNN 中實現的分層分解和數據移動策略。 CUTLASS 最新版本為3.3&#xff0c;相比1.3.3變動較大。然而重溫一下1.3.3仍然…

生產問題 Recv-Q101

生產上服務端口 Recv-Q101 新請求到服務器的失敗&#xff0c;幸好及時發現&#xff0c;通過重啟服務之后得到解決&#xff0c;具體原因等待排查 目前覺得的原因是&#xff1a;某些請求暫用時間比較久

Linux超簡單部署個人博客

1 安裝halo 1.1 切換到超級用戶 sudo -i 1.2 新建halo文件夾 mkdir ~/halo && cd ~/halo 1.3 編輯docker-compose.yml文件 vim ~/halo/docker-compose.yml 英文輸入法下&#xff0c;按 i version: "3"services:halo:image: halohub/halo:2.10container_…

2017年全國碩士研究生入學統一考試管理類專業學位聯考數學試題——解析版

文章目錄 2017 級考研管理類聯考數學真題解析一、問題求解&#xff08;本大題共 5 小題&#xff0c;每小題 3 分&#xff0c;共 45 分&#xff09;下列每題給出 5 個選項中&#xff0c;只有一個是符合要求的&#xff0c;請在答題卡上將所選擇的字母涂黑。真題&#xff08;2017-…

Python 提高篇學習筆記(一):深拷貝和淺拷貝

文章目錄 一、什么是對象的引用二、深拷貝和淺拷貝2.1 淺拷貝(Shallow Copy)2.2 深拷貝(Deep Copy)2.3 copy.copy和copy.deepcopy的區別 一、什么是對象的引用 在 Python 中&#xff0c;對象的引用是指變量指向內存中某個對象的地址或標識符。當你創建一個新的對象(比如一個整…

[技術雜談]計算機系統硬件類名稱

在各種編程語言都可以見到利用WMI查詢計算機硬件信息&#xff0c;因此知道有哪些計算機硬件名稱非常有必要&#xff0c;下面列舉了所有計算機硬件名稱可以查詢。 本文內容 冷卻設備類輸入設備類大容量存儲類主板、控制器和端口類 顯示另外 6 個 計算機系統硬件類別將表示硬…

git修改遠程分支名稱

先拉取old_branch最新代碼到本地 git checkout old_branchgit pull origin old_branch本地修改后并推送 git branch -m old_branch new_branch # 修改分支名稱git push --delete origin old_branch # 刪除在遠程的老分支推送新分支 git push origin new_branch本地分支與遠…

除自身以外數組的乘積[中等]

優質博文&#xff1a;IT-BLOG-CN 一、題目 給你一個整數數組nums&#xff0c;返回數組answer&#xff0c;其中answer[i]等于nums中除nums[i]之外其余各元素的乘積。題目數據保證數組nums之中任意元素的全部前綴元素和后綴的乘積都在32位整數范圍內。請不要使用除法&#xff0…

【Qt開發流程】之富文本處理

描述 Scribe框架提供了一組類&#xff0c;用于讀取和操作結構化的富文本文檔。與Qt中以前的富文本支持不同&#xff0c;新的類集中在QTextDocument類上&#xff0c;而不是原始文本信息。這使開發者能夠創建和修改結構化的富文本文檔&#xff0c;而不必準備中間標記格式的內容。…

【數據結構】A : A DS圖_傳遞信息

A : A DS圖_傳遞信息 Description 小明在和他的小伙伴們玩傳消息游戲&#xff0c;游戲規則如下&#xff1a; 有n名玩家&#xff0c;所有玩家編號分別為0~n-1&#xff0c;其中小明編號為0&#xff1b;每個玩家都有固定的若干個可傳信息的其他玩家(也可能沒有)。傳消息的關系是…

busybox制作根文件系統2

上篇內容使用busybox制作好了根文件系統&#xff0c;接下來需要進行一些測試和功能的完善&#xff01; 根文件系統的測試 測試根文件系統的時候不是直接燒寫到EMMC里面&#xff0c;這樣測試效率太低了&#xff0c;Ubuntu的rootfs目錄已經保存了根文件系統&#xff0c;只需要在…

向量數據庫,展望AGI時代

無論是向量數據庫&#xff0c;還是大模型&#xff0c;歸根結底&#xff0c;大家在追捧它時的心態&#xff0c;焦慮大于需求。 向量數據庫的熱潮&#xff0c;在一定程度上“外化”了人們的焦慮。 但這并不能否定向量數據庫的實際價值&#xff0c;甚至更長遠來看&#xff0c;向…

【C++】linux下的gdb程序調試

目錄 【C】Linux 下的 GDB 程序調試1. 安裝 GDB2. 編譯程序3. 啟動 GDB4. 設置斷點5. 執行程序6. 調試命令7. 調試崩潰8. 結束調試 【C】Linux 下的 GDB 程序調試 在開發 C 程序時&#xff0c;出現 bug 是常見的。調試是找出程序錯誤的關鍵步驟之一。在 Linux 環境下&#xff…

RedisTemplate使用詳解

RedisTemplate介紹StringRedisTemplate介紹RedisConnectionFactory介紹RedisConnectionFactory源碼解析 RedisOperations介紹RedisOperations源碼解析 RedisTemplate使用連接池配置RedisTemplate連接池連接池配置 RedisTemplate應用場景RedisTemplate主要特點RedisTemplate使用…

redis運維(十六) 有序集合

一 有序集合 把握一點&#xff1a; 各種redis 命令都提供各種語言對應的API 接口,后續API是關鍵 ① 概念 1、sorted set --> 有序集合2、redis有序集合也是集合類型的一部分&#xff0c;所以它保留了集合中元素不能重復的特性3、但是不同的是,有序集合給每個元素多設置…