C++11特性補充

目錄

lambda表達式

定義

捕捉的方式

可變模板參數

遞歸函數方式展開參數包

數組展開參數包

移動構造和移動賦值

包裝器

綁定bind

智能指針

RAII

auto_ptr

unique_ptr

shared_ptr

循環引用

weak_ptr

補充

總結

特殊類的設計

不能被拷貝的類

只能在堆上創建類

將構造函數私有

將析構函數私有

只能在棧上創建對象

?只能創建一個對象(單例模式)

餓漢模式

懶漢模式

單例對象的釋放


本文是對上一篇文章《C++11新特性》的補充,將會介紹過很多的C++11特性。

C++11新特性-CSDN博客文章瀏覽閱讀987次,點贊30次,收藏30次。本文對C++11重要特性進行介紹,重點解析了C++中的左值引用和右值引用的區別,幫助讀者快速了解C++11新特性以及這些特性的使用方法。 https://blog.csdn.net/2401_87944878/article/details/147116766

lambda表達式

定義

C++中可調用對象有三種:函數,仿函數,以及lambda表達式。

函數的類型寫起來太繁瑣了,所以一般都會使用仿函數來替代函數,但是對于實現簡單的函數實現,使用仿函數代碼長,比較笨重;因此引出了lambda表達式來解決這一問題。

lambda表達式包含三個部分:1)捕捉列表;2)函數參數;3)函數返回值類型;4)函數體。

struct Add1
{int operator()(int x, int y){return x + y;}
};
auto Add2 = [](int x, int y)->int {return x + y  ;};
Add1()(1, 1);
Add2(1, 1);

以上分別是仿函數和lambda表達式實現加法函數,可以看出lambda表達式的使用更簡約。

對于lambda表達式,如果沒有參數,參數部分可以省略;返回值類型也可以不寫,讓編譯器自動推導。

auto Add3 = [](int x, int y) {return x + y; };

函數指針---在C++中人不用就不用,其類型寫起來不太方便;

仿函數---是一個類,重載了operator();?

lambda---表達本質上是一個局部匿名函數對象。

lambda表達式的函數體內可以直接調用全局變量,但是不能直接調用局部變量或局部函數。如果需要使用局部對象就要將其添加至捕捉列表中。

void test_02()
{double rate = 0.5;auto Add3 = [rate](int x, int y) {return (x + y)*rate; };}

如上圖,rate就實現了捕捉,在lambda表達式中可以只用rate。

捕捉的方式

捕捉的方式有四種:

1)[var],對var進行值捕捉;

2)[&var],對var進行引用捕捉,lambda表達式中var的改變會影響其外部的var;

3)[ = ],對局部變量和函數進行全部值捕捉;

4)[ & ],對局部變量和函數進行全部引用捕捉。

當然捕捉列表也可以混合起來使用。

int a = 1, b = 2;
int c = 10;
auto Add4 = [&, c] { a++, b++;cout << c; };

以上就是混合捕捉:對所有局部變量進行引用捕捉,除了c使用值捕捉。

lambda表達式的底層實際上還是仿函數。

lambda表達式在進行值捕捉的時候,默認捕捉后的類型是const修飾的,也就是說進行值捕捉后的參數是不能進行修改的,如果想要修改需要添加mutable關鍵字

int a = 1, b = 2;
auto Add4 = [a, b]()mutable { a++, b++;cout << a << b; };

?使用mutable后,值捕捉的變量就就可以實現修改了。


可變模板參數

在C語言中就已經有可變參數的概念了,比如printf和scnaf的參數都屬于可變參數;C++引入了模板概念,自然也有了可變模板參數概念。?

在C++11新增了可變模板參數,使得模板中參數可以不是固定的了,但是可變模板參數使用起來不太方便,此處我們進行簡略介紹。

模板的參數是類型,函數的參數是對象,要將這兩點分開。

template<class ... Args>
void Show(Args ... args)
{sizeof...(args);//....
}

模板中的Args指的是模板參數包,函數中的args指的是函數參數包;其中...表示其是可變參數,此處需要注意省略號的位置;通過sizeof...(args)可以打印出參數的個數。

參數包的語法不支持直接使用args[i],所以參數包是不能直接使用的,需要先展開再使用;

遞歸函數方式展開參數包

通過遞歸依次減少參數,直到參數為零,其與參數遞歸類似,只不過遞歸停止的條件是參數。

template<class T>
void Show(T val)
{cout << val << endl;
}
template<class T ,class ... Args> 
void Show(T val ,Args ... args)
{cout << val << " ";Show(args...);
}
void test_03()
{Show(1);Show(1,"x");Show(1, "x", 1.3);
}

如圖,上面通過兩個重載函數來實現參數包的展開,參數大于1會優先匹配void Show(T val ,Args ... args);當參數等于1的時候就去匹配void Show(T val),此時遞歸結束。

數組展開參數包

template<class T>
int Show(T val)
{cout << val << " ";return 0;
}
template<class ... Args> 
void Show(Args ... args)
{int arr[] = { Show(args)... };cout << endl;
}
void test_03()
{Show(1);Show(1,"x");Show(1, "x", 1.3);
}

int arr[] = { Show(args)... };數組元素個數是沒有給定的所以需要通過對數組元素進行計數,此時就需要將參數包展開;


移動構造和移動賦值

C++11新增了兩個默認成員函數:移動構造和移動賦值。

移動構造和移動賦值都屬于移動語義,其是為了解決對于返回值的參數無法直接引用問題。

無析構函數+無拷貝構造+無賦值重載+無移動構造的情況下,編譯器會生成默認移動構造:對于內置類型進行淺拷貝,對于內置類型會去調用其自己的拷貝構造。

移動賦值也同理。

一般要寫析構函數的內是深拷貝的類,需要寫拷貝構造和賦值重載;


包裝器

C++11引入了包裝器function,通過包裝器可以實現將函數,仿函數以及lambda表達式類型統一。

void Print1()
{cout << "hello world" << endl;
}
struct Print2
{void operator()(){cout << "hello world" << endl;}
};
void test_04()
{auto Print3 = [] {cout << "hello world" << endl; };cout << typeid(Print1).name() << endl;cout << typeid(Print2()).name() << endl;cout << typeid(Print3).name() << endl;
}

以上三個可調用對象實現的功能都是一樣的,此處可以使用typeid().name打印出其三個可調用對象的類型。

可以看出三者類型是完全不同的,C++11引入了包裝器function使得可以通過函數返回值,參數將不同的可調用對象類型統一。

function<void()> arr[] = { Print1,Print2(),Print3 };

以上將三個可調用對象都放入到了數組中,其類型都是function<void()>,關于function的使用方法就是:function<函數返回值(函數參數)>,function使用時的頭文件是<functional>。

包裝器function的本質是一個適配器,可以對函數指針,仿函數以及lambda表達式進行包裝。


綁定bind

對于庫中的有些接口,參數傳遞很多并且有些參數是固定的;此時就可以通過bind綁定將接口參數進行綁定。

double RAdd(int x, int y, double rate)
{return (x + y) * rate;
}void test_05()
{//如果rate始終是固定的,不需要進行修改次數就可以使用綁定bindauto RAdd2 = bind(RAdd, placeholders::_1, placeholders::_2, 0.54);cout << RAdd2(10, 4) << endl;
}

bind的參數:可調用對象;參數匹配的位置:10就和_1位置匹配,4就和_2位置匹配,_1和_2有分別和函數參數匹配即x和y;

通過對_1和_2位置的調換,可以出現不同的結果。

如下圖所示:通過對_1和_2位置的調換可能會導致函數的調用發生變化。

?補充:對于類成員函數的綁定是不同的:對于非靜態成員函數要加&,并且要給出對象或對象地址;對于靜態成員函數可以不加&,但是建議加上。

class A
{
public:static int Add1(int x, int y){return x + y;}int Add2(int x, int y){return x + y;}private:int _a;
};void test_05()
{//非靜態成員函數的綁定auto CAdd1 = bind(&A::Add2, A(), placeholders::_1, placeholders::_2);A aa;auto CAdd2 = bind(&A::Add2, &aa, placeholders::_1, placeholders::_2);//靜態成員函數的綁定auto CAdd3 = bind(&A::Add1, placeholders::_1, placeholders::_2);}

智能指針

C++中對于錯誤的處理是使用拋異常的方式解決的,拋異常就會導致進程流被修改,當進程流改變后,原本需要釋放的內存沒有走到delete就會導致內存泄漏。

為了解決這一問題可以采用多次拋異常,在拋異常前進行空間的釋放,如果一個函數中有多個位置進行了空間開辟,當拋異常后還需要考慮哪一個需要釋放,這就導致了情況多,代碼繁瑣的問題。?

能不能一個空間開辟后,出作用域就銷毀呢???此時就可以使用智能指針來實現。

?智能指針通過將指針交給對象,讓對象進行資源的管理,在對象出作用域時調析構函數進行資源的釋放。

template<class T>
class Ptr
{
public:Ptr(T* ptr):_ptr(ptr){}Ptr(const Ptr& p):_ptr(p._ptr){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}~Ptr(){delete _ptr;}
private:T* _ptr;
};

以上是對智能指針的簡單模擬實現,但是在使用的時候,對指針進行拷貝構造時就會出現對同一塊區域的多次釋放。在C++中有不同的智能指針,不同的指針處理方式也是不同的。?

RAII

資源獲取即初始化,利用對象生命周期來控制資源,在構造函數時獲取資源,在對象析構時銷毀資源。

優勢:1)不需要顯式釋放空間,出作用域自動銷毀;

2)對象所需的資源在其生命周期類持續有效;

auto_ptr

auto_ptr是C++98時推出的一個智能指針;auto_ptr對于指針拷貝的方法是:將空間的管理權轉移,在完成拷貝構造之后,將被拷貝對象置空。其底層實現如下:

template<class T>
class auto_ptr
{
public:auto_ptr(T* ptr):_ptr(ptr){}auto_ptr(const auto_ptr& p):_ptr(p._ptr){p._ptr = nullptr;}auto_ptr operator=(const auto_ptr& p){_ptr = p._ptr;p._ptr = nullptr;return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}~auto_ptr(){delete _ptr;}
private:T* _ptr;
};

可以看到,在拷貝完成后,被拷貝對象懸空導致其無法使用,這使得auto_ptr不被廣泛使用。

unique_ptr

unique_ptr對于拷貝和賦值的處理方法更加暴力,unique_ptr不支持賦值和拷貝構造。

所以在實現的時候,需要將拷貝和賦值只聲明不定義,并且置為私有防止其在外面被定義,為防止編譯器自動生成在函數后面加上delete。

template<class T>
class unique_ptr
{
public:unique_ptr(T* ptr):_ptr(ptr){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}~unique_ptr(){delete _ptr;}
private:unique_ptr(const unique_ptr& p) = delete;unique_ptr operator=(const unique_ptr& p) = delete;T* _ptr;
};

shared_ptr

shared_ptr通過對指向每個空間的指針進行計數來實現對空間的釋放,當一個指向一個空間的指針數量為0是對空間進行釋放;

所以shared_ptr需要添加一個成員變量來實現計數。

template<class T>
class shared_ptr
{
public:shared_ptr(T* ptr):_ptr(ptr),_pcount(new int(1)){}shared_ptr(const shared_ptr& p):_ptr(p._ptr),_pcount(p._pcount){++(*_pcount);}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}shared_ptr operator=(const shared_ptr& p){_ptr = p._ptr;_pcount = p._pcount;++(*_pcount);return *this;	}~shared_ptr(){if (--(*_pcount) == 0){delete _ptr;}}
private:T* _ptr;int* _pcount;
};

shared_ptr確實解決了何時釋放的問題,但是又出現了循環引用的問題。

循環引用

當一個對象有前后指針的時候就會出現問題,比如一下代碼。

template<class T>
struct Node
{Node():_a(T()),_next(nullptr),_prev(nullptr){ }T _a;shared_ptr<Node> _next;shared_ptr<Node> _prev;
};void test_06()
{shared_ptr<Node<int>> aa(new Node<int>);shared_ptr<Node<int>> bb(new Node<int>);aa->_next = bb;bb->_prev = aa;
}

當aa的后繼指針指向bb的時候,bb的引用計數就變成了2,同理aa的引用計數也編程了2。當對aa進行銷毀的時候,其引用計數是2無法調用Node的析構,也就不能進行_next和_prev的析構了,此時就出現了空間泄露。

為了解決這一問題添加了weak_ptr。

weak_ptr

weak_ptr與shared_ptr的使用是一樣的,只是在對shared_ptr拷貝的時候不會對引用計數進行改變。weak_ptr不遵循RAII,其是專門為解決循環引用而出現的。

template<class T>
class weak_ptr
{
public:weak_ptr(T* ptr):_ptr(ptr){}weak_ptr(const shared_ptr<T>& p):_ptr(p._ptr){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}weak_ptr operator=(const shared_ptr<T>& p){_ptr = p._ptr;return *this;}
private:T* _ptr;
};

上面Node類定義變為:

template<class T>
struct Node
{Node():_a(T()),_next(nullptr),_prev(nullptr){ }T _a;weak_ptr<Node> _next;weak_ptr<Node> _prev;
};

補充

shared_ptr<int> aa(new int[10]);

當我們開辟一個數組的空間時,釋放時再使用delete就不能進行空間釋放了,所以對于shared_ptr和unique_ptr的參數增加了一個:釋放對象。

結合lambda表達式使用起來更加簡單。Del的類型可以使用function表示。

template<class T, class Del=function<void(T*)>>
class shared_ptr
{
public:shared_ptr(T* ptr, Del del = [](T* ptr) {delete ptr; }):_ptr(ptr),_pcount(new int(1)),_del(del){}shared_ptr(const shared_ptr<T>& p):_ptr(p._ptr),_pcount(p._pcount){++(*_pcount);}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}shared_ptr operator=(const shared_ptr<T>& p){_ptr = p._ptr;_pcount = p._pcount;++(*_pcount);return *this;}~shared_ptr(){if (--(*_pcount) == 0){_del(_ptr);}}
private:T* _ptr;int* _pcount;Del _del;
};
shared_ptr<int> a(new int[10], [](int* ptr) {delete[] ptr; });
shared_ptr<FILE> b(fopen("test.txt", "w"), [](FILE* ptr) {fclose(ptr); });

總結

auto_ptr:管理權轉移,被拷貝的對象會懸空;

unique_ptr:不支持賦值和拷貝構造;

shared_ptr:通過引用計數控制內存的釋放;

weak_ptr:解決shared_ptr的循環引用問題,拷貝和賦值的時候不增加引用計數。


特殊類的設計

不能被拷貝的類

C++11增加了delete的功能,不僅能對new的空間進行釋放,放在默認成員函數后面還能表示:不讓編譯器生成該函數。

實現:將拷貝和賦值置為私有,且使用delete不讓編譯器生成即可。

template<class T>
class NoCopy
{
public://...
private:NoCopy(const NoCopy&) = delete;NoCopy operator=(const NoCopy&) = delete;T _val;
};

只能在堆上創建類

也就是不能再棧上創建對象,棧上創建對象必須能夠調用構造函數和析構函數,所以對于類的實現有兩種方法:1)將構造函數私有;2)將析構函數私有;還需要將拷貝構造和賦值刪除。

將構造函數私有

將構造函數私有之后,new也不能調用構造函數了,所以需要增加接口專門返回在對上創建的對象。

template<class T>
class HeapOnly
{static HeapOnly* Get(){return new HeapOnly;}private:HeapOnly(const HeapOnly&) = delete;HeapOnly operator=(const HeapOnly&) = delete;HeapOnly(){//...}T _val;
};

將析構函數私有

將析構函數私有之后,還需要提供接口進行空間的釋放。

template<class T>
class HeapOnly
{HeapOnly(){//...}private:~HeapOnly(){//...}HeapOnly(const HeapOnly&) = delete;HeapOnly operator=(const HeapOnly&) = delete;T _val;
};

只能在棧上創建對象

對于只能在棧上創建對象,就需要不能讓new正常使用,new分為兩步:1)調用operator new;2)調用構造函數;

實現:重載operator new,將其刪除并將構造函數私有;向外提供構造接口。

template<class T>
class StackOnly
{static StackOnly Creat(){StackOnly st;return st;}~StackOnly(){//...}
private:void* operator new(size_t size) = delete;StackOnly(){//...}T _val;
};

?只能創建一個對象(單例模式)

創建一個類只能創建一個對象,這中類稱為單例模式。

實現方法:1)只創建一個對象,將這個對象設為類的及靜態成員,在每次獲取的時候都將這一靜態成員返回;2)將構造函數設為私有,防構造;3)防拷貝,將拷貝和賦值刪除。

該類分為兩種:餓漢模式和懶漢模式。

餓漢模式

餓漢模式指的是在main函數之前就創建出對象。

class Hungry_class
{
public:Hungry_class& Get(){return _date;}private:Hungry_class(const Hungry_class&) = delete;Hungry_class operator=(Hungry_class) = delete;Hungry_class(){//...}static Hungry_class _date;
};
Hungry_class Hungry_class::_date;

函數自始至終都只有一個對象_date。

餓漢模式的劣勢:

1)在main函數之前就進行對象的創建,當有多個單例模式需要被創建時就會影響程序的啟動效率。

2)如果兩個單例類A和B之間相互聯系,B的創建需要A,如果還是使用餓漢模式就會導致無法控制哪一個單例對象先創建。

懶漢模式

懶漢模式與餓漢不同的是:懶漢模式是在需要單例對象時才去創建。

實現方法:與餓漢模式一樣,但是懶漢的成員變量是指針而不是對象。

class Lazy_class
{
public:Lazy_class& Get(){if (_date == nullptr){Lazy_class* _date = new Lazy_class;}return *_date;}private:Lazy_class(const Lazy_class&) = delete;Lazy_class operator=(Lazy_class) = delete;Lazy_class(){//...}T _val;static Lazy_class* _date;
};
Lazy_class* Lazy_class::_date;

單例對象的釋放

一般單例對象是不需要進行釋放的,但是如果中途需要進行顯示釋放或程序需要進行一些特殊動作(如持久化)等;

單例對象通常是在多個進程中使用的,所以單例對象的釋放不能簡單的根據作用域讓其自己釋放,需要提供專門的接口釋放。

單例模式析構可以手動調用,也可以像智能指針一樣自動調用。

可以通過設計一個內部類實現自動調用析構函數。

class Lazy_class
{
public:static Lazy_class& Get(){if (_date == nullptr){Lazy_class* _date = new Lazy_class;}return *_date;}static void Destory(){//...}struct GC{~GC(){Lazy_class::Destory();}};private:Lazy_class(const Lazy_class&) = delete;Lazy_class operator=(Lazy_class) = delete;Lazy_class(){//...}static Lazy_class* _date;static GC _gc;
};
Lazy_class* Lazy_class::_date;
Lazy_class::GC Lazy_class::_gc;

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

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

相關文章

My SQL 索引

核心目標&#xff1a; 理解 mysql 索引的工作原理、類型、優缺點&#xff0c;并掌握創建、管理和優化索引的方法&#xff0c;以顯著提升數據庫查詢性能。 什么是索引&#xff1f; 索引是一種特殊的數據庫結構&#xff0c;它包含表中一列或多列的值以及指向這些值所在物理行的指…

極狐GitLab 注冊限制如何設置?

極狐GitLab 是 GitLab 在中國的發行版&#xff0c;關于中文參考文檔和資料有&#xff1a; 極狐GitLab 中文文檔極狐GitLab 中文論壇極狐GitLab 官網 注冊限制 (BASIC SELF) 您可以對注冊實施以下限制&#xff1a; 禁用新注冊。新注冊需要管理員批準。需要用戶電子郵件確認。…

10.(vue3.x+vite)div實現tooltip功能(css實現)

1:效果截圖 2:代碼實現 <template><div><div class="tooltip" style="margin-top: 20%; margin-left: 20%; background-color: blueviolet; color: white;

Linux下 文件的查找、復制、移動和解壓縮

1、在/var/log目錄下創建一個hehe.log的文件&#xff0c;其文件內容是&#xff1a; myhostname ghl mydomain localdomain relayhost [smtp.qq.com]:587 smtp_use_tls yes smtp_sasl_auth_enable yes smtp_sasl_security_options noanonymous smtp_sasl_tls_security_opt…

Ubuntu 安裝 Docker 教程(官方推薦方式)

? 步驟 1&#xff1a;卸載舊版本&#xff08;如果有&#xff09; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done---### ? 步驟 2&#xff1a;更新 APT 索引并安裝依賴項bash sudo a…

計算機視覺與深度學習 | Transformer原理,公式,代碼,應用

Transformer 詳解 Transformer 是 Google 在 2017 年提出的基于自注意力機制的深度學習模型,徹底改變了序列建模的范式,解決了 RNN 和 LSTM 在長距離依賴和并行計算上的局限性。以下是其原理、公式、代碼和應用的詳細解析。 一、原理 核心架構 Transformer 由 編碼器(Encod…

計算機基礎 | 常見進制與單位簡介 / 表示 / 描述

注&#xff1a;本文為 “進制與常見單位應用” 相關文章合輯。 原文為繁體&#xff0c;注意術語描述差異。 略作重排。 進制簡介&#xff08;二進制、八進制、十進制、十六進制&#xff09; 發表于 2017-01-20 鄭中勝 數字系統&#xff08;Numeral system&#xff09;&#…

門面模式與適配器模式

一、門面模式 門面模式&#xff1a;提供統一接口訪問子系統接口 1、包含角色 外觀系統對外的統一接口子系統類的集合&#xff1b;并不知道外觀角色的存在&#xff0c;需要為了配合外觀角色而做特殊處理或修改 2、舉例 原本開關燈要分別操作各個房間的燈&#xff0c;現在設置總…

SpringBoot Actuator指標收集:Micrometer與Prometheus集成

文章目錄 引言一、Spring Boot Actuator基礎二、Micrometer簡介與集成三、基本指標收集與配置四、自定義業務指標實現五、與Prometheus集成六、實戰案例&#xff1a;API性能監控總結 引言 在現代微服務架構中&#xff0c;監控應用程序的健康狀況和性能指標變得至關重要。Sprin…

【Android面試八股文】Android應用進程的啟動流程【二】

應用進程 1.1 Android系統進程的啟動過程&#xff1a; 1、init進程fork出Zygote進程后&#xff0c;Zygote進程會創建一個服務端socket&#xff0c;等待AMS發起socket請求。 同時&#xff0c;由Zygote進程fork出的SystemServer進程會啟動各項系統服務&#xff0c;其中就包含了A…

基于Django的AI客服租車分析系統

基于Django的AI客服租車分析系統 【包含內容】 【一】項目提供完整源代碼及詳細注釋 【二】系統設計思路與實現說明 【三】AI智能客服與用戶交互指導手冊 【技術棧】 ①&#xff1a;系統環境&#xff1a;Python 3.8&#xff0c;Django 4.2框架 ②&#xff1a;開發環境&a…

全同態加密醫療數據分析集python實現

目錄 摘要一、前言二、全同態加密與醫療數據分析概述2.1 全同態加密(FHE)簡介2.2 醫療數據分析需求三、數據生成與預處理四、系統架構與流程4.1 系統架構圖五、核心數學公式六、異步任務調度與(可選)GPU 加速七、PyQt6 GUI 設計八、完整代碼實現九、自查測試與總結十、展望…

linux 搭建 dvwa 滲透測試環境

linux 安裝 dvwa 1、分為4個部分&#xff0c;搭建dvwa滲透測試環境2、安裝centos 7.63、安裝apache http server4、安裝mysql5、安裝php6、運行dvwa 1、分為4個部分&#xff0c;搭建dvwa滲透測試環境 本文基于centos 7.6 搭建 dvwa 滲透測試環境 安裝一個linux系統安裝apache…

stm32(gpio的四種輸出)

其實GPIO這個片上外設的功能&#xff1a; 用于控制IO引腳。 CPU就如同大腦&#xff0c;而這些片上外設就如同四肢一樣的關系 如圖 —————————————————————————————— OK類比了以上 其實GPIO是有 八種工作模式的 這八種工作模式 因為GPIO是面向IO…

Flask(3): 在Linux系統上部署項目

1 前言 說實話&#xff0c;我并不想接觸linux系統&#xff0c;要記住太多的命令。我更習慣windows系統&#xff0c;鼠標點點&#xff0c;只要記住少量的命令就可以了。 但是我選擇了python&#xff0c;就注定無法逃避linux系統。雖然python也能在windows上很好的運行&#xff0…

WIN10重啟開機不用登錄,直接進入桌面

我們個人機不需要登錄。 步驟1 置&#xff0c;帳戶&#xff0c;登錄選項&#xff0c;密碼。 輸入當前密碼后&#xff0c;直接下一步。 再次重啟&#xff0c;就會發現不需要密碼了。

idea中導入從GitHub上克隆下來的springboot項目解決找不到主類的問題

第一步&#xff1a;刪除目錄下的.idea和target&#xff0c;然后用idea打開 第二步&#xff1a;如果有需要&#xff0c;idea更換jdk版本 原文鏈接&#xff1a;https://blog.csdn.net/m0_74036731/article/details/146779040 解決方法&#xff08;idea中解決&#xff09;&#…

數字友好戰略視域下數字安全核心要素的理論解構與實踐路徑

本論文聚焦數字友好戰略框架下的數字安全核心要素&#xff0c;系統闡述數字安全的理論內涵、戰略價值與實踐路徑。通過多維度分析數字安全在個人、企業與國家層面的作用機制&#xff0c;結合國際法規標準與前沿技術實踐&#xff0c;揭示數字安全對構建可持續數字生態的關鍵支撐…

管理與維護samba服務器

允許 Linux、Unix 系統與 Windows 系統之間進行文件和打印機共享&#xff0c;使得不同操作系統的用戶能夠方便地訪問和共享資源&#xff0c;就像在同一局域網中的 Windows 計算機之間共享資源一樣。 server01安裝Samba服務器 [rootserver ~]# rpm -qa | grep samba [rootserver…

前端面試每日三題 - Day 8

這是我為準備前端/全棧開發工程師面試整理的第八天每日三題練習&#xff0c;涵蓋 JavaScript 閉包與執行上下文、React 性能優化與虛擬 DOM、以及高可用消息隊列架構設計。 ? 題目 1&#xff1a;深入理解 JavaScript 中的閉包與執行上下文 &#x1f4d8; 解析&#xff1a; 閉…