CppCon 2014 學習: The Implementation of Value Types

“The Implementation of Value Types” 在C++里,通常指的是如何設計和實現**值類型(value types)**的類,確保它們符合值語義(value semantics),也就是說:

  • 對象的賦值和拷貝操作應該是深拷貝(deep copy)而非淺拷貝(shallow copy),這樣每個對象都有自己獨立的狀態。
  • 支持拷貝構造函數拷貝賦值運算符移動構造函數移動賦值運算符,保證高效且安全的對象管理。
  • 設計時要注意資源管理(內存、文件句柄等),防止資源泄漏。
  • 支持異常安全,保證異常發生時對象狀態一致。
  • 常用做法是遵循“五法則(rule of five)”:自定義或默認聲明構造函數、析構函數、拷貝/移動構造函數和賦值運算符。

具體實現上,值類型通常包括:

  1. 默認構造函數(default constructor)
    創建一個有效的初始狀態對象。
  2. 拷貝構造函數(copy constructor)
    用另一個對象初始化新對象,實現深拷貝。
  3. 拷貝賦值運算符(copy assignment operator)
    把另一個對象的值賦給當前對象,實現深拷貝并且處理自賦值問題。
  4. 移動構造函數(move constructor)
    從臨時對象“偷取”資源,提高性能。
  5. 移動賦值運算符(move assignment operator)
    類似移動構造,賦值時“偷取”資源。
  6. 析構函數(destructor)
    釋放對象所擁有的資源。

典型例子 — 一個簡單的值類型類

class MyValue {
private:int* data;size_t size;
public:// 默認構造MyValue(size_t n = 0) : data(n ? new int[n]() : nullptr), size(n) {}// 拷貝構造MyValue(const MyValue& other) : data(other.size ? new int[other.size] : nullptr), size(other.size) {std::copy(other.data, other.data + size, data);}// 拷貝賦值MyValue& operator=(const MyValue& other) {if (this != &other) {int* new_data = other.size ? new int[other.size] : nullptr;std::copy(other.data, other.data + other.size, new_data);delete[] data;data = new_data;size = other.size;}return *this;}// 移動構造MyValue(MyValue&& other) noexcept : data(other.data), size(other.size) {other.data = nullptr;other.size = 0;}// 移動賦值MyValue& operator=(MyValue&& other) noexcept {if (this != &other) {delete[] data;data = other.data;size = other.size;other.data = nullptr;other.size = 0;}return *this;}// 析構函數~MyValue() {delete[] data;}
};

”值類型的幾個關鍵特性,我幫你總結和解釋一下:

值類型(Value Types)的定義要點:

  • 對象的身份(Identity)不重要
    也就是說,兩個值相同的對象,雖然它們的內存地址不同,但在語義上是一樣的,沒有區別。
  • 對象的地址不影響它的操作
    你操作對象時,不依賴于它的具體存儲地址。換句話說,復制對象后,無論操作原對象還是復制對象,結果應當是一樣的。
  • 對對象的操作是語義上的深拷貝
    當你復制一個值類型對象,得到的是其內容的完整拷貝(deep copy),不是簡單的指針拷貝(shallow copy)。因此修改復制品不會影響原對象。
  • 操作獨立于上下文
    對某個對象進行操作時,不會影響或依賴于其他上下文環境,即操作是局部的、獨立的。
  • 操作不會改變上下文
    這個“上下文”指的是外部環境或對象狀態,操作對象不會引發意外的副作用或全局變化。

簡單理解:

值類型強調的是數據的值本身,而不是對象的身份或位置。你可以自由復制、傳遞值類型對象,不用擔心副作用或引用混淆。
比如,內置類型 intdouble,或 std::string 都是值類型:復制它們,修改副本不會影響原件。

“最熟悉的值類型”:

最熟悉的值類型:

  • 內置算術類型是值類型的典型代表
    比如 intdoublechar 等,都是非常典型的值類型。

  • 它們不引用其他狀態(無指針或引用指向其他數據)
    也就是說,它們的數據完全由自身存儲,不依賴外部資源。

  • 它們即使沒有地址,也依然有意義
    例如,字面量常量 42 雖然沒有固定的內存地址,但依然可以作為有效的值。

  • 字符串也被用作值類型
    但字符串稍微復雜一點,因為它們內部實現常常會有指向動態內存的指針,所以嚴格來說它們的值語義沒有那么純粹。

  • 字符串作為值類型的情況有點復雜
    例如,std::string 通過拷貝會復制內容,但底層可能有優化(如小字符串優化),而且它管理動態內存,存在深淺拷貝的問題。

內置算術類型的屬性

  • 操作明晰
    對這些類型的操作(如加減乘除、比較等)是被廣泛理解和定義明確的。

  • 高效的操作
    硬件層面直接支持,執行速度快。

  • 性質清晰
    其行為和數學上的算術操作一致,沒有隱藏的復雜性。

  • 支持字面量
    可以直接寫成常量,比如 42, 3.14

  • 支持常量初始化
    可以在編譯時確定其值,方便編譯優化。

  • 可以作為非類型模板參數
    在模板編程中,可以用它們作為模板參數,比如 std::array<int, 10> 中的 10

  • 緊湊的表示
    占用內存小,通常和機器字長匹配。

  • 高效的復制和移動
    拷貝或傳遞時成本極低,基本上就是內存復制。

  • 高效的函數參數傳遞
    傳遞時不涉及復雜操作,適合傳值調用。

  • 相對不容易產生別名問題
    由于沒有指針等間接引用,內存別名問題較少。

  • 并發友好
    讀寫時不會涉及復雜同步,適合多線程環境。

  • 極易被編譯器優化
    編譯器可以很好的對算術類型做優化,如寄存器分配、常量折疊。

  • 在一定范圍內具備可移植性
    雖然不同平臺的具體大小和表示可能有差異,但基本行為一致。

Ad-Hoc Representation(臨時/隨意表示法) 的用法,下面解釋一下:

Ad-Hoc Representation(臨時表示)

  • 定義變量時直接用基本類型表示概念
    例如:
    int apple_quality = 3;
    int orange_quality = 4;
    
    這里,apple_qualityorange_quality 都只是用 int 來表示“質量”的數值。
  • 直接用基本操作和比較表達邏輯
    例如:
    if (apple_quality + orange_quality) { ... }
    
    表示如果兩個水果的質量之和不為零,就執行某操作。
  • 字符串直接表示顏色邊界
    std::string border = "green";
    if (border == "#00FF00") { ... }
    
    用字符串直接表示顏色,進行比較。

重點理解:

  • 這種做法是快速且簡單的,直接用內置類型或簡單類型來表示實際概念(質量、顏色等)。
  • 這種表示方法沒有額外封裝或抽象,所有語義都隱含在變量名和代碼邏輯里。
  • 缺點是缺乏類型安全和明確的語義,容易發生混淆或錯誤(比如不小心把 apple_qualityorange_quality 交換使用,編譯器不會報錯)。

這段內容解釋了為什么很多人不愿意定義新的值類型(new value types),主要是基于“恐懼”(FEAR)和對抽象的擔憂。具體來說:

為什么不定義新的值類型?

擔憂(Concerns with Abstraction):

  1. 它會太慢(It will be too slow)
    覺得使用新類型或封裝會影響性能,不如直接用基礎類型快。
  2. 它會花太長時間去寫(It will take too long to write)
    認為創建新類型需要寫很多樣板代碼,開發成本高。
  3. 它會有bug(It will have bugs)
    擔心自己寫的新類型不夠完善,容易引入新的錯誤。
  4. 它會花太長時間去學習(It will take too long to learn)
    覺得新抽象需要花費時間去理解和掌握,學習曲線陡峭。
  5. 它不會達到預期效果(It will not do what is expected)
    擔心新類型沒法滿足實際需求,無法正確表達業務邏輯。
  6. 它在其他地方不可用(It will not be available elsewhere)
    擔心定義的新類型缺乏通用性,無法在其他項目或代碼庫中復用。

1. 字面量類型(Literal Types)的用途

  • 可用于定義編譯期常量
    例如constexpr變量必須是字面量類型。
  • 可用于計算數組大小
    例如數組大小必須是編譯期常量,字面量類型的值可以用來指定數組大小。
  • 可用于非類型模板參數
    模板參數可以是整型常量等字面量類型,從而實現模板的泛化和編譯期計算。

2. 什么是字面量類型?

字面量類型包括:

  • 標量類型(scalar type),比如int, double等基本類型。
  • 引用類型(reference type),如int&
  • 字面量類型的數組,比如int[5]
  • 字面量類類型(literal class type),即滿足以下條件的類:
    • 所有非靜態數據成員和基類都是字面量類型。
    • 是聚合類型(aggregate)或至少有一個constexpr構造函數(且不是拷貝或移動構造函數)。
    • 具有平凡的(trivial)析構函數。

3. 字面量值示例(用戶自定義字面量)

示例定義了一個自定義字面量操作符operator""_p,用于創建probability類型的對象:

probability operator""_p(long double v) {return probability(v);
}
probability x;
probability y = x * 0.3_p;  // 0.3_p會調用operator""_p(0.3)
  • 這里0.3_p是一個字面量表達式,編譯器會調用operator""_p,將數字0.3轉換為probability類型。
  • 這種寫法使代碼更直觀,語義更明確。
  • 字面量類型是允許在編譯期使用和計算的類型,關鍵用于高效、安全的編譯期編程。
  • 字面量類類型需要滿足嚴格條件,才能被constexpr構造和使用。
  • 用戶定義字面量允許自定義類型與數字字面量自然結合,提升代碼的可讀性和表達力。

這段代碼和“Redundancy(冗余)”這個標題一起出現,含義和理解如下:

代碼說明

template <typename T>
struct read_mostly_complex {T real, imaginary;T angle, magnitude;....
};
  • 這是一個模板結構體read_mostly_complex,表示一個復數(complex number)的數據結構。
  • 它包含四個成員變量:
    • realimaginary:復數的實部和虛部。
    • anglemagnitude:復數的極坐標形式的角度和幅度。

“Redundancy”(冗余)含義

這里的“冗余”指的是:

  • 結構體中同時保存了復數的兩種表示方法
    • 直角坐標系(real, imaginary)
    • 極坐標系(angle, magnitude)
  • 這兩種表示其實是互相可以轉換的,保存兩種數據會產生數據重復(冗余)。

冗余的潛在問題

  • 同步復雜性
    當復數被修改時,需要保證這兩種表示都正確更新,否則數據不一致。
  • 增加內存消耗
    保存多余的成員占用更多空間。
  • 維護難度
    代碼必須處理如何正確同步這兩個表示,容易出錯。

什么時候可能會用冗余?

  • 如果讀操作遠多于寫操作,預先計算并緩存兩種表示(比如極坐標角度和幅度)可以提升讀性能。
  • 但要付出同步和維護的代價。

你給的這段“Padding(內存填充)”內容,結合列出的類型,是在說明C++中不同基本數據類型在內存中的對齊和大小,這直接影響了結構體或類的內存布局及性能。

主要內容理解

  • **Padding(內存填充)**是指編譯器為了滿足CPU對內存訪問的對齊要求,在數據成員之間或末尾自動插入的空白字節(填充字節)。
  • 這段列舉了常見的基本類型,按大致大小和對齊要求排序,通常從大到小排列:
    • 較大類型(對齊要求高)
      • double, int64_t, long long
      • pointer(指針大小依平臺,64位通常是8字節)
      • long
    • 中等大小類型
      • float, int32_t, char32_t
      • int
    • 較小類型
      • int16_t, char16_t, short
      • char
    • 特殊類型
      • long double
      • ptrdiff_t
      • size_t

為什么這很重要?

  • 內存對齊

    • CPU讀取內存一般要求數據按一定字節對齊,未對齊訪問可能導致性能下降或硬件異常。
    • 編譯器根據數據類型自動安排數據成員的位置,并可能插入填充字節以保證對齊。
  • 結構體大小和內存浪費

    • 填充字節會增加結構體大小,導致內存利用率下降。
    • 通過調整成員順序,可以減少填充,提高內存緊湊度。

舉個例子

struct Example {char c;        // 1字節int  i;        // 4字節,通常需要4字節對齊
};
  • 編譯器會在char c后面插入3個填充字節,使int i從對齊的地址開始。
  • 整個結構體大小可能是8字節,而不是5字節。

這段“Hotness”的內容主要講的是緩存局部性(cache locality)和數據結構設計對性能的影響,特別是如何設計數據類型(struct/class)來提升CPU緩存的利用效率。

主要內容理解

  • Cache use has a large impact on performance.
    CPU緩存對性能影響很大,訪問緩存命中數據比訪問主內存快得多。

  • Minimize the number of cache lines your type typically uses.
    盡量減少你的類型(結構體/類)占用的緩存行數。緩存行一般是64字節(具體大小因CPU而異),占用越少,緩存命中率越高。

  • Put hot and cold fields on different cache lines.
    把“熱”數據(頻繁訪問的成員)和“冷”數據(很少訪問的成員)放到不同緩存行。這樣能避免頻繁訪問熱數據時加載冷數據,節省緩存空間。

  • Put fields accessed together on the same cache line.
    把經常一起訪問的成員放到同一緩存行,利用空間局部性原則,提高緩存命中率。

舉例說明

假設你有一個游戲角色的類,里面有:

  • 熱字段(hot fields):當前血量、位置、速度,游戲循環中每幀都會訪問
  • 冷字段(cold fields):角色描述信息、創建時間、統計數據,更新頻率低

優化思路:

  • 把熱字段放在一起,冷字段放在另一塊內存區域(比如用不同的結構體或者通過內存對齊技巧)
  • 這樣CPU緩存加載時,訪問熱數據不會加載冷數據,減少緩存污染,性能更好。

總結

  • **“Hotness”**強調的是程序數據的訪問頻率與緩存效率的關系。
  • 合理設計數據結構,提升緩存局部性,是提高程序性能的關鍵技巧之一。
  • 這是系統性能優化和高性能編程中非常重要的原則。

Trivially Copyable(平凡可復制類型) 的概念,下面是詳細中文理解:

Trivially Copyable(平凡可復制類型)

  • 基本類型都是平凡可復制的
    比如 int, double, 指針等,這些類型的數據可以直接按位復制(bit-blast),不需要調用構造函數或賦值運算符。
  • 內容可以直接按位拷貝
    這意味著你可以用 memcpy 或直接復制內存的方式復制對象,拷貝不會出錯,也不會破壞對象狀態。
  • 內容可以傳遞到寄存器中
    這些類型在函數調用時可以直接通過寄存器傳遞,效率更高。
  • 可以通過 std::is_trivially_copyable<T>::value 來檢測
    C++11 標準庫 <type_traits> 提供了檢測類型是否是平凡可復制類型的工具。
#include <type_traits>
if (std::is_trivially_copyable<TYPE>::value) {// TYPE 是平凡可復制的,可以安全地按位復制
}

為什么重要?

  • 平凡可復制類型允許高效復制,不用調用復雜的構造函數或賦值函數。
  • 在內存操作(比如序列化、拷貝數組、網絡傳輸)時更安全和高效。
  • 編譯器可以做更多優化。

類型(對象)變得很大時該怎么辦,重點在于性能優化,特別是內存訪問的局部性和效率:

如果類型很大怎么辦?

  • 訪問的局部性(locality of access)通常比節省空間更重要
    也就是說,能快速訪問內存中的相關數據,比起僅僅節約內存空間更能提升性能。
  • 但這并不總是成立:如果你的類型過大怎么辦?
    過大的對象會導致復制開銷變大,影響效率。

解決方案:

  1. 邏輯復制而非物理復制(Copy-On-Write,寫時復制)

    • 復制時不立刻復制整個對象的數據,只是復制引用(指針)。
    • 只有當要修改對象時,才實際復制數據。
    • 這樣避免了不必要的內存復制,提高效率。
  2. 引用計數(Reference Counting)

    • 通過計數管理共享對象的生命周期。
    • 對于非循環結構非常有效。
    • 但是必須確保引用計數是線程安全的(thread friendly),避免競態條件。
  3. 通過嵌入小型值來提升局部性

    • 對于小的數據,直接存儲在對象內部,而不是通過引用計數指向外部大塊內存。
    • 這樣可以減少內存訪問的跳轉,提升訪問速度。

總結來說,就是在設計大類型對象時,要權衡性能和內存開銷,利用寫時復制引用計數技術,同時保持線程安全,并盡可能優化內存訪問的局部性。

這段代碼展示了一個類的拷貝構造函數和移動構造函數的實現示例,重點是資源管理(假設類內部通過指針 p 指向某個內容 content)。解釋如下:

type(const type& a): p(new content(*a.p)) {
}
  • 拷貝構造函數
    • 參數是 const type& a,表示從另一個同類型對象 a 拷貝構造。
    • 這里通過 new content(*a.p),為新的對象分配了新的內存,并拷貝了 a 對象所指向內容的值(深拷貝)。
    • 這樣兩個對象各自擁有獨立的資源,互不干擾。
type(type&& a): p(a.p) {a.p = std::nullptr;
}
  • 移動構造函數
    • 參數是 type&& a,表示接收一個右值引用,即臨時對象或即將被銷毀的對象 a
    • 將指針 p 直接“偷取”自 a,不進行深拷貝,避免資源復制的開銷。
    • 接著將 a.p 設為 nullptr,使得原對象不再擁有這塊資源,防止析構時重復釋放。

總結:

  • 拷貝構造函數做深拷貝,分配新內存,復制內容。
  • 移動構造函數做資源“搬移”,直接轉移指針,避免復制,提升性能。

這句話的意思是:在C++中傳遞參數時,主要有兩種常用方式:

1. 按值傳遞 (Pass by value)

  • 傳遞參數時,會復制參數的值。
  • 函數內部操作的是參數的副本,不影響調用者的原始數據。
  • 適用于小型、簡單的數據類型,比如內置類型(int、char、float等)。
  • 對于較大或復雜對象,復制開銷較大。

2. 按常量引用傳遞 (Pass by const reference)

  • 傳遞參數時,傳遞的是參數的引用(地址),避免了復制開銷。
  • 使用 const 關鍵字保證函數內部不會修改傳入的對象。
  • 適合大型對象或復雜類型,避免性能損失。
  • 保證函數內部不修改傳入的參數,增加代碼安全性。

何時用哪種?

  • 小型內置類型(如 int, double 等)用 按值傳遞,效率高且簡單。
  • 大型對象或復雜類型按常量引用傳遞,避免復制帶來的性能開銷。

Slicing(對象切片)

class B {virtual bool d() { return false; }
};
class D : public B {virtual bool d() { return true; }
};bool g(B a) { return a.d(); }          // 傳值調用
bool h(const B& a) { return a.d(); }   // 傳引用調用g(D()) == false && h(D()) == true
解釋:
  • 傳值調用 g(B a)
    參數是基類對象B的值,傳入派生類D時,會發生 對象切片(slicing)
    對象切片意味著:雖然傳入的是D,但只復制了B部分,D特有的成員和行為被“切掉”了。
    因此調用a.d()調用的是B::d(),返回false

  • 傳引用調用 h(const B& a)
    參數是B的引用,傳入D對象時,不會發生切片。
    a實際指向的是D對象,所以調用的是D::d(),返回true

傳值(Pass by Value)示例函數

extern type va1(type input);
extern type va2(type input);void vf1(type& output, type input) {output += va1(input);output += va2(input);
}
  • 這里vf1函數有兩個參數:

    • outputtype類型的引用,函數中會修改它。
    • inputtype類型的值,傳值會復制一份。
  • 函數內部使用了input兩次傳給va1va2。因為input是值傳遞,不會影響外部變量。

總結

  • 對象切片是傳值時派生類對象被裁剪成基類對象的常見問題,導致虛函數調用變成基類版本。
  • 使用引用傳遞避免切片,保留多態行為。
  • 傳值會復制參數,傳引用則傳遞地址,性能和行為會有所不同。

我幫你總結一下這段關于“直接傳值”和“間接傳值”的內容:

直接傳值(Direct Pass by Value)

  • 只適用于trivially copyable(可平凡拷貝)的類型。
  • 參數拷貝到棧上(如 IA32 架構),本質就是 memcpy 操作。
  • 小參數可能直接拷貝到寄存器(如 AMD64 架構),但可能導致寄存器溢出。
  • 某些架構(如 SPARC32)不使用此方式。
  • 對不支持的類型或架構,建議使用間接傳值。

間接傳值(Indirect Pass by Value)

  • 支持非 trivially copyable 的類型。
  • 過程:
    1. 在調用處為參數類型創建一個臨時變量。
    2. 將傳入的實參復制到這個臨時變量。
    3. 傳遞臨時變量的指針給函數。
    4. 函數內部通過指針間接訪問參數內容。
    5. 函數返回時銷毀臨時變量。

總結

  • 直接傳值效率更高,但只能用于簡單類型。
  • 復雜類型或者大對象,使用間接傳值以避免不必要的性能開銷和拷貝錯誤。

代碼示例涉及到 C++ 中傳遞參數的方式,以及函數調用的寫法。下面我幫你逐步解析和理解:

代碼內容

extern type ra1(const type& input);
extern type ra2(const type& input);void rf1(type& output, type& input) {output += ra1(input);output += ra2(input);
}

逐行解釋

  1. extern type ra1(const type& input);

    • 這是函數聲明,表示函數 ra1 接受一個 const type& 類型的參數,返回一個 type 類型的結果。
    • const type& input 表示傳入的是對一個 type 對象的常量引用,該函數不會修改 input
    • extern 表示該函數在別的文件或模塊中定義,這里只是聲明。
  2. extern type ra2(const type& input);

    • 同理,ra2 也是一個函數,參數和返回值類型和 ra1 一致。
  3. void rf1(type& output, type& input)

    • 這是函數定義。rf1 有兩個參數,分別是 outputinput,都是 type 類型的引用(非const)。
    • 因為是非const引用,意味著函數內部可以修改這兩個對象。
  4. 函數體:

output += ra1(input);
output += ra2(input);
  • ra1(input) 調用 ra1,傳入 input,返回一個 type,然后把這個結果加到 output 上。
  • ra2(input) 同理。
  • output += ... 表明 type 類型重載了 operator+=,允許用 += 來累加。

重點理解

  • 傳遞參數方式:const type& input
    • 傳入函數的是對象的常量引用(const reference),不會復制對象,提高效率,且保證函數不會修改傳入參數。
  • 函數調用和返回
    • ra1ra2 返回新的 type 對象(或者是按值返回),用來累加到 output
  • output 是傳引用,可以被修改
    • rf1 通過引用參數修改了 output,調用后 output 的值發生了改變。

簡單總結

  • ra1ra2 函數通過 常量引用傳入參數,避免了拷貝且不修改參數。
  • rf1 函數通過引用修改 output,累加了 ra1(input)ra2(input) 的結果。
  • 這種寫法典型用于提高性能(避免拷貝)并且保證參數不會被意外修改。

你這段內容是在講 C++ 中函數參數傳遞方式的推薦準則(Parameter Passing Recommendations),我幫你整理和解釋一下:

參數傳遞方式推薦

1. 傳值(Pass by value)適合的情況:
  • 傳值時,函數參數會被拷貝一份,開銷是復制對象的成本。
  • 建議傳值的條件
    • 類型比較 (小于等于 2 個指針大小,比如 8~16 字節以內)。
    • 類型是 trivially copyable(平凡可拷貝類型),即拷貝非常簡單、開銷低,沒有復雜的拷貝構造函數或析構函數。例如內置類型、簡單的結構體等。
2. 傳常量引用(Pass by const reference)適合的情況:
  • 傳常量引用不拷貝對象,只傳引用,避免了拷貝成本。
  • 建議傳const ref的條件
    • 類型比較 ,拷貝開銷高。
    • 別名檢測(alias detection)比較廉價,意思是傳引用可能會帶來潛在的別名問題(參數和調用者共享同一內存),但只要檢測別名開銷低,這樣傳引用更好。
3. 其他情況:
  • 如果以上兩條無法判斷,建議做實驗和性能測試(profile)來決定哪種傳遞方式更好。

簡單總結

傳參方式適用情況說明
傳值小型且平凡可拷貝的類型拷貝開銷低,傳值簡單安全
傳常量引用大型對象,拷貝成本高,別名檢測便宜避免復制,效率高
其他情況無法確定,需測試通過實驗判斷性能差異

額外說明

  • “trivially copyable” 是 C++ 術語,指類型的拷貝操作就是按內存逐字節拷貝,沒有用戶定義的拷貝構造函數、析構函數等復雜操作。
  • 現代編譯器和硬件對不同傳參方式優化不同,實際性能還要考慮 CPU cache、調用約定等因素,所以建議有疑問時用實際代碼測一下性能

你這段內容主要講的是函數參數可能存在別名(aliasing)問題的處理方式和策略,我幫你逐點整理并解釋:

別名(Aliasing)問題的處理 Approaches

1. 忽略問題 (Ignore the problem)

  • 有些情況下,程序設計者選擇不管別名問題,直接寫代碼,但這可能會帶來潛在的錯誤或未定義行為。

2. 文檔說明 (Document the problem)

  • 在代碼注釋或文檔里明確指出某些參數可能會出現別名問題,提醒調用者注意。

3. 列舉可能的覆蓋 (List possible overwrites in comments)

  • 在注釋中列出可能會被修改(覆蓋)的變量,幫助理解代碼可能的副作用。

4. 使用 restrict 限定符(C++沒有,但概念上存在)

  • 在 C 語言中,restrict 用于告訴編譯器該指針是唯一訪問該內存的指針,優化編譯器生成代碼。
  • C++ 標準沒有 restrict,但有一些編譯器擴展支持。
  • 目的是告知編譯器不存在別名,從而優化代碼。

5. 克服別名問題的方法

(a) 復制可能別名的參數
void rf3(type& output, const type& input) {type temp = input;          // 復制 input 到臨時變量 tempoutput += rf1(temp);output += rf2(temp);
}
  • 通過復制參數到一個臨時變量,避免 outputinput 可能是同一個對象(別名)導致的問題。
  • 函數內部操作臨時變量 temp,安全且不破壞 input
(b) 有條件地復制
void rf3(type& output, const type& input) {if (&output == &input) {    // 判斷 output 和 input 是否是同一個對象type temp = input;output += rf1(temp);output += rf2(temp);} else {// 直接使用 input,不復制output += rf1(input);output += rf2(input);}
}
  • 只有在 outputinput 是同一個對象時才復制,避免不必要的復制,提高性能。
? 有條件地不復制(示例是賦值操作符重載)
type& type::operator=(const type& a) {if (this != &a) {      // 檢測自賦值(防止自身賦值)delete p;p = new content(*a.p);}return *this;
}
  • 這是經典的自賦值檢測,防止對象給自己賦值時誤刪內存或重復操作。
  • 自賦值時跳過操作,避免錯誤。

總結

處理方式說明
忽略不理會別名,簡單寫代碼,風險大
文檔說明明確告知可能存在別名,提醒開發者注意
注釋列出可能覆蓋讓讀者理解代碼副作用
使用 restrict (C++無)告訴編譯器無別名,優化代碼
復制參數通過復制規避別名問題
有條件復制只有當別名存在時才復制,減少開銷
有條件跳過操作(自賦值檢測)防止自賦值導致的問題

你這段內容主要講的是 避免別名(aliasing)導致的計算錯誤 的幾種常見技巧,尤其是在對象成員變量操作時的順序和緩存讀值策略。

1. 先讀后寫(Order Reads Before Writes)

void rf4(type& output, const type& input) {type temp1 = ra1(input);type temp2 = ra2(input);output += temp1;output += temp2;
}
  • 先把依賴 input 的計算結果用臨時變量存好,再統一寫入 output
  • 這樣可以防止 outputinput 可能是同一個對象導致的寫操作影響后續讀操作的問題(別名問題)。
  • 讀操作先完成,寫操作后完成,保證讀取數據時不會被寫操作破壞。

2. 別名字段問題(Aliasing Fields)

template <typename T>
T& complex<T>::operator*=(const T& a) {real = real * a.real - imag * a.imag;imag = real * a.imag + imag * a.real;return *this;
}
  • 這是復數乘法的復寫操作符,按數學定義實現:
    (real + iimag) * (a.real + ia.imag)
  • 問題: 第二行 imag = real * a.imag + imag * a.real; 使用了剛剛更新的 real,而不是乘法前的舊 real
  • 這樣會導致計算錯誤,因為 real 在第二行已經被改寫了。

3. 讀緩存字段(Read Caching Fields)

template <typename T>
T& complex<T>::operator*=(const T& a) {T a_real = a.real, a_imag = a.imag;T t_real = real, t_imag = imag;real = t_real * a_real - t_imag * a_imag;imag = t_real * a_imag + t_imag * a_real;return *this;
}
  • 解決上面的問題,把用到的變量提前緩存(copy)到臨時變量里。
  • t_realt_imag 緩存乘法前的 realimaga_reala_imag 緩存參數 a 的成員變量。
  • 后續計算用緩存變量,避免讀到已修改的成員變量,保證正確性。

總結

技巧目的解釋
先讀后寫防止寫操作影響后續讀操作先把讀結果存到臨時變量,再寫入目標,避免數據被覆蓋導致錯誤
直接操作成員變量簡單明了,但可能因寫操作破壞讀數據而出錯成員變量被修改后后續計算使用了錯誤數據
緩存讀的成員變量先緩存舊值,避免讀寫沖突使用臨時變量緩存原始數據,計算時使用緩存,避免順序錯誤和別名影響
你理解得對,這些都是常見的處理別名帶來狀態變化的安全策略。如果你想,我可以幫你寫更多關于別名安全和性能權衡的示例代碼,也可以幫你解釋其他類似的技巧。你有興趣嗎?

你這段內容講的是程序設計中的沖突(Conflicts)問題,特別是全局狀態和靜態初始化順序相關的注意點,我幫你整理和詳細解釋:

1. 全局狀態(Global State)

關鍵點:

  • 全局狀態的修改不應影響邏輯運算的結果

    • 程序的邏輯計算結果應與全局狀態的變化無關,保證程序行為的可預測性和穩定性。
  • 訪問常量狀態(constant state)是安全的

    • 讀取全局常量數據不會引發競態或數據錯誤。
  • 內存分配是允許的

    • 申請和釋放內存本身允許修改全局內存狀態,但通常需要線程安全。
  • 物理共享狀態必須保護(線程安全)

    • 如果多個線程訪問全局共享狀態,需要使用鎖、原子操作等機制保證同步。
  • 操作不得影響全局狀態

    • 邏輯運算操作應避免修改全局可變狀態,保持純凈(pure)或無副作用(side-effect free)。
  • I/O操作僅限于調試和性能分析

    • 生產邏輯中避免用I/O,以免引入非確定性行為。

2. 靜態初始化順序(Static Initialization Order)

代碼示例:

constexpr type::type(int arg): field(arg) { }type v(3);
  • 這是一個類型 type 的構造函數,使用 constexpr 表示編譯時常量構造。
  • type v(3); 說明定義了一個全局(或靜態)變量 v,調用構造函數初始化。

相關問題:

  • 靜態初始化順序問題

    • 全局/靜態對象的初始化順序在不同編譯單元間是不確定的,可能導致訪問尚未初始化的對象。
    • 這可能導致運行時錯誤或未定義行為。
  • constexpr 構造函數

    • 保證對象可以在編譯期初始化,減少運行時順序依賴。

總結

方面說明
全局狀態修改全局狀態不應影響邏輯運算;讀常量全局狀態安全;共享狀態必須線程安全保護。
靜態初始化順序多個全局/靜態對象初始化順序可能不確定,需避免依賴順序;constexpr可用來確保編譯期初始化。

如果你需要,我可以幫你詳細講講怎么解決靜態初始化順序問題(比如用“構造函數靜態局部變量”技巧),或者幫你寫線程安全訪問全局狀態的示例代碼,你想聽哪方面?

你這段內容涉及并發安全(Concurrency)和異常安全(Exception Safety),主要講如何寫線程安全且異常安全的代碼,特別是在賦值操作符重載時。下面幫你詳細拆解和解釋:

并發(Concurrency)

1. 減少別名(aliasing)

  • 減少參數別名,避免多個引用指向同一個可變對象,從而引起數據競爭。
  • 這樣可以降低并發讀寫沖突的風險。

2. const 引用參數是并發讀安全的

  • 因為是只讀訪問,沒有修改,多個線程并發讀取是安全的。

3. 深度成員(deep argument)只有讀訪問或訪問受鎖保護

  • 參數中復雜對象(比如指針指向的內容)如果只是讀取,沒有修改,是線程安全的。
  • 如果修改,必須用鎖(mutex)或者原子操作保護,保證數據一致性和線程安全。

異常安全(Exception Safety)

4. 盡量讓操作 noexcept(不拋異常)

  • 不拋異常的操作更容易保證程序穩定和簡單。
  • 如果不能,必須保證異常安全。

5. 異常安全要求

  • 異常發生時,保證對象狀態恢復到操作前的狀態(強異常保證)。
  • 避免出現部分修改導致對象處于不一致狀態。

代碼示例分析

6. 先分配,再修改(Allocate Before Changes)

type& type::operator=(const type& a) {if (this != &a) {content *q = new content(*a.p);  // 先分配內存和復制內容delete p;                        // 再釋放舊資源p = q;                          // 指針指向新內容}return *this;
}
  • 先新申請空間、復制數據,確保新數據準備好。
  • 再刪除舊內容,最后修改指針指向。
  • 防止在刪除舊數據后申請失敗,導致對象無效。
  • 但這段代碼在 delete p 時如果拋異常會出問題。

7. 異常安全的資源恢復(Recover Resources)

type& type::operator=(const type& a) {if (this != &a) {content *q = new content(*a.p);try {delete p;} catch (...) {delete q;  // 避免內存泄漏throw;     // 繼續拋出異常}p = q;}return *this;
}
  • try-catch 捕獲 delete p 可能拋出的異常(雖然一般 delete 不會拋,但理論上可能)。
  • 如果 delete 拋異常,先釋放新申請的內存 q,避免泄漏。
  • 然后重新拋出異常,保證異常向上傳遞。
  • 這種寫法保證了異常安全性。

總結

方面要點
并發安全減少別名;const引用安全;深度成員只讀或鎖保護
異常安全盡量noexcept;異常時對象狀態恢復;防止資源泄漏
賦值運算符示例先申請新資源,后釋放舊資源;捕獲異常,釋放新資源,重新拋出異常
你理解得很對,這些都是編寫高質量 C++ 并發安全且異常安全代碼的重要技巧。如果你需要,我可以幫你寫更詳細的線程安全示例,或者異常安全的其他場景示范。需要嗎?

性能優化(Optimization)中的職責劃分和具體策略,幫你詳細解釋和總結:

優化職責(Responsibilities)

(程序員)負責:

  • 選擇數據的表示方式(choose the representation)
    設計數據結構,決定如何在內存中組織和存儲數據。

  • 實現操作(implement the operations)
    編寫算法和函數,完成數據處理。

  • 減少別名(reduce aliasing)
    通過避免多個引用或指針指向同一數據,減少編譯器優化時的阻礙。

  • 減少內存訪問次數(reduce memory accesses)
    內存訪問是性能瓶頸,減少不必要的加載和存儲可以顯著提升效率。

編譯器負責:

  • 完成絕大多數其他優化工作
    包括指令調度、寄存器分配、循環展開、內聯等復雜優化。

避免冗余內存訪問(Avoid Redundant Memory Access)

  • 重復加載同一個指針效率低下
    如果多次訪問相同指針(地址),每次都從內存讀,會浪費時間。除非你等待別人修改它,否則盡量緩存。

  • this 指針是隱式指針,會限制優化
    成員函數里訪問成員變量本質上是通過 this 指針訪問的,編譯器必須假設指針可能發生變化,影響優化。

  • 積極緩存字段的讀取(Aggressively cache field reads)
    先把成員變量讀到臨時變量中,后續使用臨時變量,避免多次通過 this 指針訪問內存。

  • 寫回緩存字段(Write back cached field)
    修改緩存的變量后,適時寫回到成員變量,保證數據一致。

總結

優化方面說明
程序員職責設計數據表示、實現算法、減少別名和內存訪問
編譯器職責負責絕大多數低層和復雜優化
減少重復內存訪問不要重復加載同一指針,除非必要
緩存字段讀取先讀取到局部變量緩存,避免多次訪問內存
寫回緩存緩存變量修改后,及時同步回成員變量

這段內容講的是 函數內聯(Inlining)的原則和注意事項,我幫你詳細整理和解釋:

函數內聯(Inlining)

關鍵點:

  • 內聯可能是長期的技術債務
    一旦內聯了函數,后續維護和修改時,內聯的代碼會散布在多個調用點,增加代碼維護復雜度。

  • constexpr 函數隱含內聯
    編譯器通常會把 constexpr 函數作為內聯處理,保證在編譯期執行。

  • 內聯會導致代碼膨脹(code bloat)
    復制函數體到多個調用處,會增大最終可執行文件體積,影響緩存效率。

  • 內聯會增加緩存壓力
    代碼膨脹使得CPU指令緩存(I-cache)壓力增大,可能反而降低性能。

  • 建議內聯的情況

    • 函數體不大于調用點開銷時(簡單函數,比如一兩行語句)
    • 有性能數據證明內聯帶來好處時(比如消除函數調用開銷顯著)

總結

原則說明
長期承諾一旦內聯,修改函數代碼可能影響多處調用點
constexpr隱含內聯constexpr函數默認編譯期展開
代碼膨脹風險內聯導致代碼體積增大,可能影響緩存性能
內聯時機函數體小于調用開銷時,或有明確性能提升時內聯

代碼編寫和維護的幾個“跟隨”原則,以及 選擇可移植類型(Portable Types) 的建議,幫你詳細解釋:

跟隨原則(Follow Along)

  • 跟隨標準(Follow the standard)
    遵循 C++ 標準規范寫代碼,保證代碼跨平臺、跨編譯器的兼容性。

  • 跟隨編譯器(Follow the compilers)
    了解和使用當前主流編譯器的最佳實踐和特性,避免使用不兼容的語法或行為。

  • 跟隨作者(Follow the authors)
    閱讀并遵循代碼庫原作者的設計思想、代碼風格和約定,避免破壞整體設計。

  • 跟隨工具(Follow the tools)
    利用靜態分析工具、格式化工具、測試工具等輔助編程,提高代碼質量和一致性。

選擇可移植類型(Choose Portable Types)

  • 明確類型大小和符號
    例如 int64_t 明確是64位有符號整數,適合存儲較大整數,確保跨平臺大小一致。

  • 避免使用平臺依賴的類型
    比如不要直接用 int 表示大數據索引,因為 int 大小可能因平臺不同而異。

  • 示例:

int64_t num_humans;  // 明確64位整數,保證跨平臺一致
for (size_t i = 0; i < v.size(); ++i)... v[i] ...;    // 用 size_t 遍歷容器,保證足夠的范圍和平臺安全int c = getchar();   // 使用標準C庫函數,保證跨平臺輸入讀取

總結

跟隨原則說明
標準遵守語言和庫的標準規范
編譯器了解和利用編譯器特性和限制
作者尊重并繼承代碼設計和風格
工具用好輔助工具提高代碼質量
選擇類型說明
明確大小和符號用如 int64_tsize_t 這類標準類型,避免平臺差異
避免隱式假設不用假設 int 大小,避免潛在溢出或錯誤

這段內容是在總結為什么要投入時間精心設計一個“值類型(value type)”,以及這樣做會帶來的好處,分別用“Invest(投入)”和“Profit(收益)”兩個部分來說明。我們一起來理解:

Invest(投入)

一個好的值類型(如類 Vector, Matrix, Money, Date 等)是值得投入時間開發的,原因如下:

你需要考慮多個方面:

  1. 操作(operations)

    • 支持哪些功能,比如加法、比較、賦值等。
  2. 屬性(properties)

    • 不變性(immutable)?線程安全?有無單位?有無符號?
  3. 通用性(generality)

    • 是否能泛型化使用?是否支持不同場景/平臺?
  4. 表示(representation)

    • 用什么成員變量表示內部數據?比如數組還是結構體?是否有壓縮?
  5. 拷貝與移動(copy and move)

    • 是否自定義拷貝構造函數和移動構造函數,以提高效率?
  6. 參數與返回值(parameters, results)

    • 用值傳遞、引用傳遞還是智能指針?是否使用 const
  7. 別名(aliasing)

    • 如何避免兩個引用指向同一對象時的副作用?
  8. 沖突(conflicts)

    • 線程安全、全局狀態、異常處理等如何管理?
  9. 優化(optimization)

    • 如何減少不必要的內存訪問、拷貝、函數調用?
  10. 可移植性(portability)

    • 在不同平臺/編譯器/操作系統下是否都能正常工作?

Profit(收益)

雖然開發一個高質量的值類型代價較高,但回報非常可觀:

  1. 減少用戶代碼開發時間(Reduced client development time)

    • 其他人用你的類型時更輕松,不需要關心底層細節。
  2. 語義清晰(Semantics are clarified early)

    • 類型的含義和規則很明確,用戶更容易理解和正確使用。
  3. 早期發現錯誤(Many mistakes are caught earlier)

    • 編譯器會檢查不合法用法,避免運行期出錯。
  4. 抽象更容易調試(Abstraction handles help debugging)

    • 用更高層的類型表示邏輯,調試時更容易定位問題。
  5. 更高的執行效率(Reduced execution costs)

    • 設計得當的值類型能減少不必要的內存操作或計算。
  6. 更好的實現(Better implementations)

    • 能逐步替換底層實現而不影響外部接口,便于優化。
  7. 抽象更方便性能分析(Abstraction handles help performance analysis)

    • 使用抽象類型能集中分析性能瓶頸,更容易優化熱點路徑。

總結

投入(Invest)收益(Profit)
定義清晰操作和表示減少使用者開發時間
處理拷貝/移動/別名錯誤早發現,調試更簡單
優化和可移植性設計性能更好,實現可持續優化
設計一個好的值類型像是一次性投資,長期回報。前期多想一點,后期所有人都會受益。

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

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

相關文章

每日算法刷題Day19 5.31:leetcode二分答案3道題,用時1h

6. 475.供暖器(中等&#xff0c;學習check函數雙指針思想) 475. 供暖器 - 力扣&#xff08;LeetCode&#xff09; 思想 1.冬季已經來臨。 你的任務是設計一個有固定加熱半徑的供暖器向所有房屋供暖。在加熱器的加熱半徑范圍內的每個房屋都可以獲得供暖。現在&#xff0c;給出…

【計算機網絡】第2章:應用層—應用層協議原理

目錄 1. 網絡應用的體系結構 2. 客戶-服務器&#xff08;C/S&#xff09;體系結構 3. 對等體&#xff08;P2P&#xff09;體系結構 4. C/S 和 P2P 體系結構的混合體 Napster 即時通信 5. 進程通信 6. 分布式進程通信需要解決的問題 7. 問題1&#xff1a;對進程進行編址…

PHP+MySQL開發語言 在線下單訂水送水小程序源碼及搭建指南

隨著互聯網技術的不斷發展&#xff0c;在線下單訂水送水服務為人們所需要。分享一款 PHP 和 MySQL 搭建一個功能完善的在線訂水送水小程序源碼及搭建教程。這個系統將包含用戶端和管理端兩部分&#xff0c;用戶可以在線下單、查詢訂單狀態&#xff0c;管理員可以處理訂單、管理…

vBulletin未認證API方法調用漏洞(CVE-2025-48827)

免責聲明 本文檔所述漏洞詳情及復現方法僅限用于合法授權的安全研究和學術教育用途。任何個人或組織不得利用本文內容從事未經許可的滲透測試、網絡攻擊或其他違法行為。使用者應確保其行為符合相關法律法規,并取得目標系統的明確授權。 對于因不當使用本文信息而造成的任何直…

計算機模擬分子合成有哪些應用軟件?

參閱&#xff1a;Top 創新大獎 以下是用于計算機模擬分子合成&#xff08;包括逆合成設計、分子對接、分子動力學模擬及綜合設計平臺&#xff09;的主流應用軟件分類總結&#xff0c;結合其核心功能和應用場景進行整理&#xff1a; &#x1f52c; 一、逆合成設計與路線規劃軟件…

Excel 中的SUMIFS用法(基礎版),重復項求和

1. 首先復制篩選條件所在的列&#xff0c;去除重復項目 數據 》重復項 》刪除重復項 2. 輸入函數公式 SUMIFS(C:C,A:A,E2) 3. 選中單元格&#xff0c;通過 ShiftF3 查看函數參數 第一個參數&#xff1a;求和區域&#xff0c;要累加的值所在的區域范圍 第二個參數&#xff1a…

【xmb】內部文檔148344597

基于小米CyberDog 2的自主導航與視覺感知系統設計報告 摘要&#xff1a; 本文針對2025年全國大學生計算機系統能力大賽智能系統創新設計賽&#xff08;小米杯&#xff09;初賽要求&#xff0c;設計并實現了基于小米仿生四足機器人CyberDog 2的平臺系統方案。參賽作品利用Cyber…

從零開始理解機器學習:知識體系 + 核心術語詳解

你可能聽說過“機器學習”&#xff0c;覺得它很神秘&#xff0c;像是讓電腦自己學會做事。其實&#xff0c;機器學習的本質很簡單&#xff1a;通過數據來自動建立規則&#xff0c;從而完成預測或決策任務。 這篇文章將帶你系統梳理機器學習的知識體系&#xff0c;并用貼近生活…

springboot集成websocket給前端推送消息

一般通常情況下&#xff0c;我們都是前端主動朝后端發送請求&#xff0c;那么有沒有可能&#xff0c;后端主動給前端推送消息呢&#xff1f;這時候就可以借助websocket來實現。下面給出一個簡單的實現樣例。 首先創建一個websocketDemo工程&#xff0c;該工程的整體結構如下&a…

【清晰教程】查看和修改Git配置情況

目錄 查看安裝版本 查看特定配置 查看全局配置 查看本地倉庫配置 設置或修改配置 查看安裝版本 打開命令行工具&#xff0c;通過version命令檢查Git版本號。 git --version 如果顯示出 Git 的版本號&#xff0c;說明 Git 已經成功安裝。 查看特定配置 如果想要查看特定…

【Github/Gitee Webhook觸發自動部署-Jenkins】

Github/Gitee Webhook觸發自動部署-Jenkins #mermaid-svg-hRyAcESlyk5R2rDn {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-hRyAcESlyk5R2rDn .error-icon{fill:#552222;}#mermaid-svg-hRyAcESlyk5R2rDn .error-tex…

C語言數據結構-鏈式棧

頭文件&#xff1a;stack.h #ifndef __STACK_H__ #define __STACK_H__ #include <stdio.h> #include <stdlib.h> typedef int DataType; /* 鏈式棧節點類型 */ typedef struct staNode { DataType data; struct staNode *pNext; }StackNode; /* 鏈式棧…

M4Pro安裝ELK(ElasticSearch+LogStash+Kibana)踩坑記錄

ElasticSearch安裝&#xff0c;啟動端口9200&#xff1a; docker pull elasticsearch:8.13.0 新增配置文件elasticsearch.yml&#xff1a; cd /opt/homebrew/etc/ mkdir elasticsearch_config cd elasticsearch_config vi elasticsearch.yml cluster.name: "nfturbo…

uni-app學習筆記十六-vue3頁面生命周期(三)

uni-app官方文檔頁面生命周期部分位于頁面 | uni-app官網。 本篇再介紹2個生命周期 1.onUnload&#xff1a;用于監聽頁面卸載。 當頁面被關閉時&#xff0c;即頁面的緩存被清掉時觸發加載onUnload函數。 例如:在demo6頁面點擊跳轉到demo4&#xff0c;在demo4頁面回退不了到d…

Java互聯網大廠面試:從Spring Boot到Kafka的技術深度探索

Java互聯網大廠面試&#xff1a;從Spring Boot到Kafka的技術深度探索 在某家互聯網大廠的面試中&#xff0c;面試官A是一位技術老兵&#xff0c;而被面試者謝飛機&#xff0c;號稱有豐富的Java開發經驗。以下是他們的面試情景&#xff1a; 場景&#xff1a;電商平臺的后端開發…

機器學習算法——KNN

一、KNN算法簡介 1.KNN思想 &#xff08;1&#xff09;K-近鄰算法 根據你的“鄰居”來推斷你是什么類別 KNN算法思想&#xff1a;如果一個樣本在特征空間&#xff08;訓練集&#xff09;中的k個最相似的樣本中的大多數屬于某一個類別。則該樣本也屬于這個類別 &#xff08…

如何評估CAN總線信號質量

CAN總線網絡的性能在很大程度上取決于其信號質量。信號質量差可能導致通信錯誤&#xff0c;進而引發系統故障、效率降低甚至安全隱患。因此&#xff0c;評估和確保CAN總線信號質量是維護系統健康和可靠性的關鍵。 在CAN總線網絡中&#xff0c;數據通過雙絞線上的差分信號傳輸。…

封裝一個小程序選擇器(可多選、單選、搜索)

組件 <template><view class"popup" v-show"show"><view class"bg" tap"cancelMultiple"></view><view class"selectMultiple"><view class"multipleBody"><view class&…

2.1HarmonyOS NEXT開發工具鏈進階:DevEco Studio深度實踐

HarmonyOS NEXT開發工具鏈進階&#xff1a;DevEco Studio深度實踐 在HarmonyOS NEXT全棧自研的技術體系下&#xff0c;DevEco Studio作為一站式開發平臺&#xff0c;通過深度整合分布式開發能力&#xff0c;為開發者提供了從代碼編寫到多端部署的全流程支持。本章節將圍繞多設…

LLMs之Tool:Workflow Use的簡介、特點、安裝和使用方法、以及案例應用

LLMs之Tool&#xff1a;Workflow Use的簡介、特點、安裝和使用方法、以及案例應用 目錄 Workflow Use的簡介 1、Workflow Use的特點 2、Workflow Use的愿景和路線圖 Workflow Use的安裝和使用方法 1、安裝 2、使用方法 查看所有命令 從 Python 中使用&#xff1a; 啟動…