C++模板筆記

Cpp模板筆記

文章目錄

  • Cpp模板筆記
    • 1. 為什么要定義模板
    • 2. 模板的定義
      • 2.1 函數模板
        • 2.1.1 函數模板的重載
        • 2.1.2 頭文件與實現文件形式(重要)
        • 2.1.3 模板的特化
        • 2.1.4 模板的參數類型
        • 2.1.5 成員函數模板
        • 2.1.6 使用模板的規則
      • 2.2 類模板
      • 2.3 可變參數模板

模板是一種通用的描述機制,使用模板允許使用通用類型來定義函數或類。在使用時,通用類型可被具體的類型,如 int、double 甚至是用戶自定義的類型來代替。模板引入一種全新的編程思維方式,稱為“泛型編程”或“通用編程”。

1. 為什么要定義模板

像C/C++/Java等語言,是編譯型語言,先編譯后運行。它們都有一個強大的類型系統,也被稱為強類型語言,希望在程序執行之前,盡可能地發現錯誤,防止錯誤被延遲到運行時。所以會對語言本身的使用造成一些限制,稱之為靜態語言

與之對應的,還有動態語言,也就是解釋型語言。如javascript/python/Go,在使用的過程中,一個變量可以表達多種類型,也稱為弱類型語言。因為沒有編譯的過程,所以相對更難以調試。

強類型程序設計中,參與運算的所有對象的類型在編譯時即確定下來,并且編譯程序將進行嚴格的類型檢查。為了解決強類型的嚴格性和靈活性的沖突,也就是在嚴格的語法要求下盡可能提高靈活性,有以下方式:

例如,想要實現能夠處理各種類型參數的加法函數

以前我們需要進行函數重載(函數名相同,函數參數不同)

int add(int x, int y)
{return x + y;
}double add(double x, double y)
{return x + y;
}long add(long x, long y)
{return x + y;
}string add(string x, string y)
{return x + y;
}

在使用時看起來只需要調用add函數,傳入不同類型的參數就可以進行相應的計算了,很方便。

但是程序員為了這種方便,實際上要定義很多個函數來處理各種情況的參數。

模板(將數據類型作為參數)

上面的問題用函數模板的方式就可以輕松解決:

//希望將類型參數化
//使用class關鍵字或typename關鍵字都可以
template <class T>
T add(T x, T y)
{return x + y;
}int main(void){cout << add(1,2) << endl;cout << add(1.2,3.4) << endl;return 0;
}

函數模板的優點:

不需要程序員定義出大量的函數,在調用時實例化出對應的模板函數,更“智能”

2. 模板的定義

模板作為實現代碼重用機制的一種工具,它可以實現類型參數化,也就是把類型定義為參數,從而實現了真正的代碼可重用性。

模板可以分為兩類,一個是函數模版,另外一個是類模板。通過參數實例化定義出具體的函數或類,稱為模板函數模板類。模板的形式如下:

// 形式
template <typename/class T1, typename T2,...>	// T1,T2稱為類型參數或者模板參數

模板參數是一個更大的概念,包含了類型參數和非類型參數,這里的T1/T2屬于類型參數,代表了類型。

模板發生的時機是在編譯時

模板本質上就是一個代碼生成器,它的作用就是讓編譯器根據實際調用來生成代碼。

編譯器去處理時,實際上由函數模板生成了多個模板函數,或者由類模板生成了多個模板類

#include <iostream>
using std::cout;
using std::endl;template <class T>
T add(T x, T y) {return x + y;
}// 模板本質上就是一個代碼生成器
// 編譯器處理時,實際上由函數模板生成了多個模板函數
// 或者多個模板類
#if 0
// 編譯器生成的,而非顯式定義的
int add(int x, int y) {return x + y;
}double add(double x, double y) {return x + y;
}
#endifint main()
{cout << add(1, 2) << endl;cout << add(1.2, 1.3) << endl;return 0;
}

2.1 函數模板

由函數模板到模板函數的過程稱之為實例化

函數模板 --》 生成相應的模板函數 --》編譯 —》鏈接 --》可執行文件

以下程序實際上可以理解為生成了四個模板函數

template <class T>
T add(T t1,T t2)
{ return t1 + t2; }void test0(){short s1 = 1, s2 = 2;int i1 = 3, i2 = 4;long l1 = 5, l2 = 6;double d1 = 1.1, d2 = 2.2;cout << "add(s1,s2): " << add(s1,s2) << endl;cout << "add(i1,i2): " << add(i1,i2) << endl;cout << "add(l1,l2): " << add(l1,l2) << endl;cout << "add(d1,d2): " << add(d1,d2) << endl;  
}

上述代碼中在進行模板實例化時,并沒有指明任何類型,函數模板在生成模板函數時通過傳入的參數類型確定出(推導出)模板類型,這種做法稱為隱式實例化

我們在使用函數模板時還可以在函數名之后直接寫上模板的類型參數列表,指定類型,這種用法稱為顯式實例化

template <class T>
T add(T t1,T t2)
{ return t1 + t2; }void test0(){int i1 = 3, i2 = 4;cout << "add(i1,i2): " << add<int>(i1,i2) << endl;  //顯式實例化
}
2.1.1 函數模板的重載

函數模板的重載分為:

(1)函數模板與函數模板重載**(謹慎使用)**

(2)函數模板與普通函數重載

函數模板與函數模板重載

如果一個函數模板無法實例化出合適的模板函數(去進行顯式實例化也有一些問題)的時候,可以再給出另一個函數模板

//函數模板與函數模板重載
//模板參數個數不同,ok
template <class T> //模板一
T add(T t1,T t2)
{ 
return t1 + t2;
}template <class T1, class T2>  //模板二
T1 add(T1 t1, T2 t2)
{
return t1 + t2;
}double x = 9.1;
int y = 10;
cout << add(x,y) << endl;   //會調用模板二生成的模板函數,不會損失精度//猜測一下,這種調用方式返回的結果是什么呢?
cout << add(y,x) << endl; 

上面輸出的結果會是19,調用的仍然是模板二,但返回類型和函數的第一個參數類型相同,采用隱式實例化時根據參數推導類型,推導出返回類型應該是int型。

#include <iostream>
using std::cout;
using std::endl;template <class T>
T add(T t1, T t2) {cout << "模板一" << endl;return t1 + t2;
}template <class T1, class T2>
T1 add(T1 t1, T2 t2) {cout << "模板二" << endl;return t1 + t2;
}int main()
{   int x = 8;double y = 9.8;int z = 10;cout << add(x, y) << endl;  // 17 調用模板二cout << add(y, x) << endl;  // 17.8 調用模板二cout << add<int>(y, x) << endl;  // 17 調用模板一cout << add(x, z) << endl;  // 18 調用模板一return 0;
}

事實上,在一個源文件中定義多個通用模板的寫法應該謹慎使用(盡量避免)如果實在需要也盡量使用隱式實例化的方式進行調用,編譯器會選擇參數類型最匹配的模板(通常是參數類型需要更少轉換的模板)。

函數模板與函數模板重載:

(1)首先,名稱必須相同(顯然)

(2)模板參數列表中的模板參數在函數中所處位置不同 —— 但不建議進行這樣的重載

(3)模板參數的個數不一樣時,可以構成重載(相對常見)

函數模板與普通函數重載

普通函數優先于函數模板執行——因為普通函數更快

編譯器掃描到函數模板的實現時并沒有生成函數,只有掃描到下面調用add函數的語句時,給add傳參,知道了參數的類型,這才生成一個相應類型的模板函數——模板參數推導。所以使用函數模板一定會增加編譯的時間。

此處,就直接調用了普通函數,而不去采用函數模板,因為更直接,效率更高。

//函數模板與普通函數重載
template <class T1, class T2>
T1 add(T1 t1, T2 t2)
{
return t1 + t2;
}short add(short s1, short s2){
cout << "add(short,short)" << endl;
return s1 + s2;
}void test1(){
short s1 = 1, s2 = 2;
cout << add(s1,s2) << endl;   //調用普通函數
}

如果沒有普通函數,就會調用上面的函數模板,實例化出相應的模板函數。盡管s1/s2的類型相同,也是可以使用該模板的。

—— T1/T2并不一定非得是不同類型,能推導出即可。

當然,如果采用顯示實例化的方式調用,肯定是調用函數模板。

2.1.2 頭文件與實現文件形式(重要)

為什么C++標準頭文件沒有所謂的.h后綴?

在一個源文件中,函數模板的聲明與定義分離是可以的,即使把函數模板的實現放在調用之下也是ok的,與普通函數一致。

//函數模板的聲明
template <class T>
T add(T t1, T t2)void test1(){ int i1 = 1, i2 = 2;cout << add(i1,i2) << endl;
}//函數模板的實現
template <class T>
T add(T t1, T t2)
{return t1 + t2;
}

如果在不同文件中進行分離

如果像普通函數一樣去寫出了頭文件、實現文件、測試文件,編譯時會出現未定義報錯

//add.h
template <class T>
T add(T t1, T t2);//add.cc
#include "add.h"
template <class T>
T add(T t1, T t2)
{
return t1 + t2;
}//testAdd.cc
#include "add.h"
void test0(){
int i1 = 1, i2 = 2;
cout << add(i1,i2) << endl;
}
  • 單獨編譯“實現文件”add.cc,使之生成目標文件,查看目標文件,會發現沒有生成與add名稱相關的函數。
  • 單獨編譯測試文件testAdd.cc,發現有與add名稱相關的函數,但是沒有地址,這就表示只有聲明。

看起來和普通函數的情況有些不一樣。

從原理上進行分析,函數模板定義好之后并不會直接產生一個具體的模板函數,只有在調用時才會實例化出具體的模板函數。

解決方法 —— 在”實現文件“中要進行調用,因為有了調用才有推導,才能由函數模板生成需要的函數

//add.cc
template <class T>
T add(T t1, T t2)
{
return t1 + t2;
}//在這個文件中如果只是寫出了函數模板的實現
//并沒有調用的話,就不會實例化出模板函數
void test1(){ 
cout << add(1,2) << endl;
}

但是在“實現文件”中對函數模板進行了調用,這種做法不優雅 。

設想:如果在測試文件調用時,在推導的過程中,看到的是完整的模板的代碼,那么應該可以解決問題

//add.h
template <class T>
T add(T t1, T t2);#include "add.cc"

可以在頭文件中加上#include “add.cc”,即使實現文件中沒有調用函數模板,單獨編譯testAdd.cc,也可以發現問題已經解決。

因為本質上相當于把函數模板的定義寫到了頭文件中。

總結:

對模板的使用,必須要拿到模板的全部實現,如果只有一部分,那么推導也只能推導出一部分,無法滿足需求。

換句話說,就是模板的使用過程中,其實沒有了頭文件和實現文件的區別,在頭文件中也需要獲取模板的完整代碼,不能只有一部分。

C++的標準庫都是由模板開發的,所以經過標準委員會的商討,將這些頭文件取消了后綴名,與C的頭文件形成了區分;這些實現文件的后綴名設為了tcc

2.1.3 模板的特化

在函數模板的使用中,有時候會有一些通用模板處理不了的情況,我們可以定義普通函數或特化模板來解決。雖然普通函數的優先級更高,但有些場景下是必須使用特化模板的。它的形式如下:

  1. template后直接跟 <> ,里面不寫類型
  2. 在函數名后跟 <> ,其中寫出要特化的類型

比如,add函數模板在處理C風格字符串相加時遇到問題,如果只是簡單地讓兩個C風格字符串進行+操作,會報錯。

可以利用特化模板解決:

#include <iostream>
#include <string.h>
using std::cout;
using std::endl;template <class T>
T add(T t1, T t2) {cout << "通用模板" << endl;return t1 + t2;
}// 模板的特化 通用模板處理不了的情況
template <>
const char* add<const char*>(const char* p1, const char* p2) {char* pret = new char[strlen(p1) + strlen(p2) + 1]();strcpy(pret, p1);strcat(pret, p2);cout << "string特化模板" << endl;return pret;
}int main()
{   short x = 10;short y = 20;// 普通函數優于函數模板執行 因為普通函數更快cout << add(x, y) << endl;cout << add(1.1, 2.2) << endl;const char* p = add("hello", "world");cout << p << endl;delete [] p;return 0;
}

輸出結果:

通用模板
30
通用模板
3.3
string特化模板
helloworld

使用模板特化時,必須要先有基礎的函數模板

如果沒有模板的通用形式,無法定義模板的特化形式。因為特化模板就是為了解決通用模板無法處理的特殊類型的操作。

特化版本的函數名、參數列表要和原基礎的函數模板相同,避免不必要的錯誤。

2.1.4 模板的參數類型
  1. 類型參數

    之前的T/T1/T2等等稱為模板參數,也稱為類型參數,類型參數T可以寫成任何類型

  2. 非類型參數

    需要是整型數據, char/short/int/long/size_t等

    不能是浮點型,float/double不可以

定義模板時,在模板參數列表中除了類型參數還可以加入非類型參數。

此時,調用模板時需要傳入非類型參數的值

template <class T,int kBase>
T multiply(T x, T y){return x * y * kBase;
}void test0(){
int i1 = 3,i2 = 4;
//此時想要進行隱式實例化就不允許了,因為kBase無法推導
cout << multiply(i1,i2) << endl;  //error
cout << multiply<int,10>(i1,i2) << endl;   //ok
}

可以給非類型參數賦默認值,有了默認值后調用模板時就可以不用傳入這個非類型參數的值

template <class T,int kBase = 10>
T multiply(T x, T y){return x * y * kBase;
}void test0(){int i1 = 3,i2 = 4;cout << multiply<int,10>(i1,i2) << endl;cout << multiply<int>(i1,i2) << endl;cout << multiply(i1,i2) << endl;
}

函數模板的模板參數賦默認值與普通函數相似,從右到左,右邊的非類型參數賦了默認值,左邊的類型參數也可以賦默認值

template <class T = int,int kBase = 10>
T multiply(T x, T y){
return x * y * kBase;
}void test0(){double d1 = 1.2, d2 = 1.2;cout << multiply<int>(d1,d2) << endl;    //okcout << multiply(d1,d2) << endl;        //ok
}

第一次的調用T代表了int,這個很好理解,因為使用模板時指定了類型參數。那么第二次也會代表int嗎?

—— 結果發現返回的結果是double型的。

我們可以得出結論

優先級:指定的類型 > 推導出的類型 > 類型的默認參數

總結:在沒有指定類型時,模板參數的默認值(不管是類型參數還是非類型參數)只有在沒有足夠的信息用于推導時起作用。當存在足夠的信息時,編譯器會按照實際參數的類型去調用,不會受到默認值的影響。

2.1.5 成員函數模板

上面我們認識了普通的函數模板,實際上,在一個普通類中也可以定義成員函數模板,如下:

#include <iostream>
using std::cout;
using std::endl;class Point{
public:Point(double x, double y): _ix(x), _iy(y){}// 成員函數模板不能加上virtual修飾template <class T>T convert() {return (T)_ix + (T)_iy;}
private:double _ix;double _iy;
};int main()
{Point pp(1.4, 2.3);cout << pp.convert<int>() << endl;return 0;
}

在convert函數模板中可以訪問Point的數據成員,說明成員函數模板的使用原理同普通函數模板一樣,在調用時會實例化出一個模板成員函數。普通的成員函數會有隱含的this指針作為參數,這里生成的模板成員函數中也會有。如果定義一個static的成員函數模板,那么在其中就不能訪問非靜態數據成員。

但是要注意:成員函數模板不能加上virtual修飾,否則編譯器報錯。

因為函數模板是在編譯時生成函數,而虛函數機制起作用的時機是在運行時。

—— 如果要將成員函數模板在類之外進行實現,需要注意帶上模板的聲明

class Point
{
public:Point(double x,double y): _x(x), _y(y){}template <class T>T add(T t1);private:double _x;double _y;
};template <class T>
T Point::add(T t1)
{return _x + _y + t1;
}
2.1.6 使用模板的規則
  1. 在一個模塊中定義多個通用模板的寫法應該謹慎使用;
  2. 調用函數模板時盡量使用隱式調用,讓編譯器推導出類型;
  3. 無法使用隱式調用的場景只指定必須要指定的類型;

2.2 類模板

一個類模板允許用戶為類定義個一種模式,使得類中的某些數據成員、默認成員函數的參數,某些成員函數的返回值,能夠取任意類型(包括內置類型和自定義類型)。

如果一個類中的數據成員的數據類型不能確定,或者是某個成員函數的參數或返回值的類型不能確定,就需要將此類聲明為模板,它的存在不是代表一個具體的、實際的類,而是代表一類類。

類模板的定義形式如下:

template <class/typename T, ...>
class 類名{
//類定義......
};

實際上,我們之前已經多次見到了類模板,打開c++參考文檔,發現vector、set、map等等都是使用類模板定義的。

類模板定義

示例,用類模板的方式實現一個Stack類,可以存放任意類型的數據

——使用函數模板實例化模板函數使用類模板實例化模板類

template <class T, int kCapacity = 10>
class Stack
{
public:Stack(): _top(-1), _data(new T[kCapacity]()){cout << "Stack()" << endl;}~Stack(){if(_data){delete [] _data;_data = nullptr;}cout << "~Stack()" << endl;}bool empty() const;bool full() const;void push(const T &);void pop();T top();
private:int _top;T * _data;
};

類模板的成員函數如果放在類模板定義之外進行實現,需要注意

(1)需要帶上template模板形參列表(如果有默認參數,此處不要寫,寫在聲明時就夠了)

(2)在添加作用域限定時需要寫上完整的類名和模板實參列表

template <class T, int kCapacity>
bool Stack<T,kCapacity>::empty() const{return _top == -1;
}

定義了這樣一個類模板后,就可以去創建存放各種類型元素的棧

void test() {Stack<string, 20> ss;Stack<int> st;Stack<> st2;
}

2.3 可變參數模板

可變參數模板(variadic templates)是 C++11 新增的最強大的特性之一,它對參數進行了高度泛化,它能表示0到任意個數、任意類型的參數。由于可變參數模板比較抽象,使用起來需要一定的技巧,所以它也是 C++11 中最難理解和掌握的特性之一。

回想一下C語言中的printf函數,其實是比較特殊的。printf函數的參數個數可能有很多個,用…表示,參數的個數、類型、順序可以隨意,可以寫0到任意多個參數。

可變參數模板和普通模板的語義是一樣的,只是寫法上稍有區別,聲明可變參數模板時需要在typename 或 class 后面帶上省略號 “…”

template <class ...Args>  
void func(Args ...args);//普通函數模板做對比
template <class T1,class T2>
void func(T1 t1, T2 t2);

Args叫做模板參數包,相當于將 T1/T2/T3/…等類型參數打了包

args叫做函數參數包,相當于將 t1/t2/t3/…等函數參數打了包

省略號寫在參數包的左邊,代表打包

例如,我們在定義一個函數時,可能有很多個不同類型的參數,不適合一一寫出,就可以使用可變參數模板的方法。

——試驗:希望打印出傳入的參數的內容

就需要對參數包進行解包。每次解出第一個參數,然后遞歸調用函數模板,直到遞歸出口

#include <iostream>
using std::cout;
using std::endl;
// 遞歸的出口
void print() {cout << endl;
}// 可變參數模板
template <class T, class ...Args>
void print(T x, Args ...args) {cout << x << " ";print(args...);	// 省略號寫在參數包的右邊,代表解包
}int main()
{print(1, "hello", 1.34, "world");return 0;
}

省略號寫在參數包的右邊,代表解包

如果沒有準備遞歸的出口,那么在可變參數模板中解包解到print()時,不知道該調用什么,因為這個模板至少需要一個參數。

遞歸的出口可以使用普通函數或者普通的函數模板,但是規范操作是使用普通函數

(1)盡量避免函數模板之間的重載;

(2)普通函數的優先級一定高于函數模板,更不容易出錯。

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

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

相關文章

遞歸函數(斐波那契數列0,1,1,2,3,5,8,13,21,34,55...)

目錄 一、斐波那契數列&#xff08;兔子問題&#xff09; 二、迭代法&#xff08;用while循環推下一項 ) 三、遞歸函數 (函數的定義中調用函數自身的一種函數定義方式) 四、遞歸函數的底層邏輯推理 (二叉樹推倒最左下節點回退法) 一、斐波那契數列&#xff08;兔子問題&…

光的本質(以暗物質維度粒子為介質的能量傳導)

一、光的概要描述 1、光的本質是能量傳導 空間中均勻分布著暗物質維度粒子。光不是粒子也不是波,而是沒有質量和形態的能量,在臨近暗物質粒子之間的一種能量傳遞。 2、光能傳遞類似牛頓鐘擺(空間中的牛頓鐘擺) 當光能能量騷動一個暗物質粒子后,該暗物質粒…

Open CASCADE學習|管道殼體生成

一、引言 在計算機輔助設計&#xff08;CAD&#xff09;和計算機圖形學領域&#xff0c;OpenCASCADE 是一款功能強大的開源 3D 建模庫。它提供了豐富的幾何和拓撲建模工具&#xff0c;其中管道殼體&#xff08;Pipe Shell&#xff09;生成是其重要功能之一。管道殼體廣泛應用于…

JS正則表達式介紹(JavaScript正則表達式)

文章目錄 JavaScript正則表達式完全指南正則表達式基礎元字符與特殊字符基本元字符. - 點號\d - 數字\D - 非數字\w - 單詞字符\W - 非單詞字符\s - 空白字符\S - 非空白字符 正則表達式標志常用標志詳解g - 全局匹配i - 忽略大小寫m - 多行匹配s - 點號匹配所有字符u - Unicod…

Kubernetes 使用 containerd 實現 GPU 支持及 GPU Operator 部署指南

目錄 Kubernetes 使用 containerd 實現 GPU 支持及 GPU Operator 部署指南 一、為什么 containerd 是趨勢&#xff1f; 二、目標 三、前提條件 四、方式一&#xff1a;containerd nvidia-container-toolkit&#xff08;基礎方式&#xff09; 1?? 安裝 NVIDIA Containe…

leetcode 2918. 數組的最小相等和 中等

給你兩個由正整數和 0 組成的數組 nums1 和 nums2 。 你必須將兩個數組中的 所有 0 替換為 嚴格 正整數&#xff0c;并且滿足兩個數組中所有元素的和 相等 。 返回 最小 相等和 &#xff0c;如果無法使兩數組相等&#xff0c;則返回 -1 。 示例 1&#xff1a; 輸入&#xf…

猿人學第十二題-js入門

1. 鏈接 https://match.yuanrenxue.cn/match/12 2. 抓包分析 2.1. m參數 通過觀察&#xff0c;只有m參數要解決&#xff1a; 3. 逆向分析 3.1. 跟棧 直接跟棧吧&#xff0c;一下就出結果了&#xff1a; 可以看到m其實很簡單&#xff0c;就是固定字符串 當前頁數&#xf…

雙系統電腦中如何把ubuntu裝進外接移動固態硬盤

電腦&#xff1a;win11 ubuntu22.04 實體機 虛擬機&#xff1a;VMware17 鏡像文件&#xff1a;ubuntu-22.04.4-desktop-amd64.iso 或者 ubuntu20.4的鏡像 外接固態硬盤1個 一、首先win11中安裝vmware17 具體安裝方法&#xff0c;網上很多教程 二、磁盤分區 1.在筆…

202535| Kafka架構與重要概念+冪等性+事務

好的&#xff01;以下是關于 Kafka 架構 以及其 重要概念 的詳細介紹&#xff0c;結合 Mermaid 圖形 和 表格&#xff0c;幫助你更好地理解各個概念的關系和作用。 Kafka 架構與重要概念 Kafka 是一個分布式消息系統&#xff0c;廣泛應用于日志收集、流處理、事件驅動架構等場…

從0開始學習大模型--Day05--理解prompt工程

提示詞工程原理 N-gram&#xff1a;通過統計&#xff0c;計算N個詞共同出現的概率&#xff0c;從而預測下一個詞是什么。 深度學習模型&#xff1a;有多層神經網絡組成&#xff0c;可以自動從數據中學習特征&#xff0c;讓模型通過不斷地自我學習不斷成長&#xff0c;直到模型…

Amazing晶焱科技:系統級 EOS 測試方法 - System Level EOS Testing Method

系統上常見的EOS測試端口以AC電源、電話線&#xff08;RJ11&#xff09;、同軸電纜&#xff08;coaxial cable&#xff09;以及以太網絡&#xff08;RJ45&#xff09;最常見&#xff0c;這些端口因有機會布線至戶外的關系&#xff0c;受到EOS/Surge沖擊的幾率也大大提升。因此電…

數據結構—(概述)

目錄 一 數據結構&#xff0c;相關概念 1. 數據結構&#xff1a; 2. 數據(Data): 3. 數據元素(Data Element): 4. 數據項&#xff1a; 5. 數據對象(Data Object): 6. 容器&#xff08;container&#xff09;&#xff1a; 7. 結點&#xff08;Node&#xff09;&#xff…

Vue 兩種導航方式

目錄 一、聲明式導航 二、編程式導航 三、兩句話總結 一、聲明式導航 1. 傳參跳轉&#xff1a; <router-link :to"/user?nameCHEEMS&id114514">Query傳參 </router-link><router-link :to"/user?參數名1參數值1&參數名2參數值2&a…

QTableWidget實現多級表頭、表頭凍結效果

最終效果&#xff1a; 實現思路&#xff1a;如果只用一個表格的話寫起來比較麻煩&#xff0c;可以考慮使用兩個QTableWidget組合&#xff0c;把復雜的表頭一個用QTableWidget顯示&#xff0c;其他內容用另一個QTableWidget。 #include "mainwindow.h" #include &qu…

2025年客運從業資格證備考單選練習題

客運從業資格證備考單選練習題 1、從事道路旅客運輸活動時&#xff0c;應當采取必要措施保證旅客的人身和財產安全&#xff0c;發生緊急情況時&#xff0c;首先應&#xff08; &#xff09;。 A. 搶救財產 B. 搶救傷員 C. 向公司匯報 答案&#xff1a;B 解析&#xff1a;…

python打卡day21

常見的降維算法 知識點回顧&#xff1a; LDA線性判別PCA主成分分析t-sne降維 之前學了特征降維的兩個思路&#xff0c;特征篩選&#xff08;如樹模型重要性、方差篩選&#xff09;和特征組合&#xff08;如SVD/PCA&#xff09;。 現在引入特征降維的另一種分類&#xff1a;無/有…

專業級軟件卸載工具:免費使用,徹底卸載無殘留!

在數字生活節奏日益加快的今天&#xff0c;我們的電腦就像每天都在"吃進"各種軟件。但您是否注意到&#xff0c;那些看似消失的程序其實悄悄留下了大量冗余文件&#xff1f;就像廚房角落里積攢的調味瓶空罐&#xff0c;日積月累就會讓系統變得"消化不良"。…

【Linux】基礎 IO(一)

&#x1f4dd;前言&#xff1a; 這篇文章我們來講講Linux——基礎IO主要包括&#xff1a; 文件基本概念回顧 C文件的操作介紹系統關于文件的基本操作 &#x1f3ac;個人簡介&#xff1a;努力學習ing &#x1f4cb;個人專欄&#xff1a;Linux &#x1f380;CSDN主頁 愚潤求學 …

Java 原生實現代碼沙箱之Java 程序安全控制(OJ判題系統第2期)——設計思路、實現步驟、代碼實現

在看這一期之前&#xff0c;需要先看上一期的文章&#xff1a; Java 原生實現代碼沙箱&#xff08;OJ判題系統第1期&#xff09;——設計思路、實現步驟、代碼實現-CSDN博客 Java 程序可能出現的異常情況 1、執行超時 占用時間資源&#xff0c;導致程序卡死&#xff0c;不釋…

常見的降維算法

作業&#xff1a; 自由作業&#xff1a;探索下什么時候用到降維&#xff1f;降維的主要應用&#xff1f;或者讓ai給你出題&#xff0c;群里的同學互相學習下。可以考慮對比下在某些特定數據集上t-sne的可視化和pca可視化的區別。 一、什么時候用到降維&#xff1f; 降維通常…