C++ Primer 交換操作

歡迎閱讀我的 【C++Primer】專欄

專欄簡介:本專欄主要面向C++初學者,解釋C++的一些基本概念和基礎語言特性,涉及C++標準庫的用法,面向對象特性,泛型特性高級用法。通過使用標準庫中定義的抽象設施,使你更加適應高級程序設計技術。希望對讀者有幫助!

在這里插入圖片描述
在這里插入圖片描述

目錄

  • 13.3交換操作
    • 編寫我們自己的swap函數
    • swap函數應該調用swap,而不是std::swap
    • 在賦值運算符中使用swap

13.3交換操作

除了定義拷貝控制成員,管理資源的類通常還定義一個名為swap的函數。對于那些與重排元素順序的算法一起使用的類,定義swap是非常重要的。這類算法在需要交換兩個元素時會調用swap。

如果一個類定義了自己的swap,那么算法將使用類自定義版本。否則,算法將使用標準庫定義的swap。雖然與往常一樣我們不知道swap是如何實現的,但理論上很容易理解,為了交換兩個對象我們需要進行一次拷貝和兩次賦值。例如,交換兩個類值HasPtr對象的代碼可能像下面這樣:

HasPtr temp=v1;//創建v1的值的一個臨時副本
v1 = v2}//將v2的值賦予v1
v2=temp;//將保存的v1的值賦子v2

這段代碼將原來v1中的string拷貝了兩次一一第一次是HasPtr的拷貝構造函數將v1拷貝給temp,第二次是賦值運算符將temp賦予v2。將v2賦予v1的語句還拷貝了原來v2中的string。如我們所見,拷貝一個類值的HasPtr會分配一個新string并將其拷貝到HasPtr指向的位置。

理論上,這些內存分配都是不必要的。我們更希望swap交換指針,而不是分配string的新副本。即,我們希望這樣交換兩個HasPtr:

string*temp=v1.ps;//為v1.ps中的指針創建一個副本
v1.ps=v2.ps;//將v2.ps中的指針賦孫v1.ps
v2.ps=temp;//將保孫的v1.ps中原來的指針賦子v2.ps

編寫我們自己的swap函數

可以在我們的類上定義一個自己版本的swap來重載swap的默認行為。swap的典型實現如下:

class HasPtr{
friend void swap(HasPtr&,HasPtr&);
//其他成員定義
};
inline
void swap(HasPtr&lhs,HasPtr&rhs)
{
using std::swap;
swap(lhs.ps,rhs.ps);//交換指針,而不是string數據
swap(lhs.i,rhs.i);//交換int成員
}

我們首先將swap定義為friend,以便能訪問HasPtr的(private的)敏據成員。由于swap的存在就是為了優化代碼,我們將其聲明為inline函數。swap的函數體對給定對象的每個數據成員調用swap。我們首先swap綁定到rhs和lhs的對象的指針成員,然后是int成員。

與拷貝控制成員不同,swap并不是必要的。但是,對于分配了資源的類,定義swap可能是一種很重要的優化手段。

swap函數應該調用swap,而不是std::swap

此代碼中有一個很重要的微妙之處:雖然這一點在這個特殊的例子中并不重要,但在一般情況下它非常重要一一swap函數中調用的swap不是std::swap。在本例中,數據成員是內置類型的,而內置類型是沒有特定版本的swap的,所以在本例中,對swap的調用會調用標準庫std::swap。

但是,如果一個類的成員有自己類型特定的swap函數,調用std::swap就是錯誤的了。例如,假定我們有另一個命名為Foo的類,它有一個類型為HasPtr的成員h。如果我們未定義Foo版本的swap,那么就會使用標準庫版本的swap。如我們所見,標準庫swap對HasPtr管理的string進行了不必要的拷貝。

我們可以為Foo編寫一個swap函數,來避免這些拷貝。但是,如果這樣編寫Foo版本的swap:

void swap(Foo& lhs,Foo &rhs)
{
//錯誤:這個函數使用了標準庫版本的swap,而不是HasPtr版本
std::swap(lhs.h,rhs.h);
//交換類型Foo的其他成員
}

此編碼會編譯通過,且正常運行。但是,使用此版本與簡單使用默認版本的swap并沒有任何性能差異。問題在于我們顯式地調用了標準庫版本的swap。但是,我們不希望使用std中的版本,我們希望調用為HasPtr對象定義的版本。

正確的swap函數如下所示:

void swap(Foo &lhs,Foo &rhs){
using std::swap;
swap(lhs.h,rhs.h);//使用HasPtr版本的swap
//交換類型Foo的其他成員
}

每個swap調用應該都是未加限定的。即,每個調用都應該是swap,而不是std::swap。如果存在類型特定的swap版本,其匹配程度會優于std中定義的版本。因此,如果存在類型特定的swap版本,swap調用會與之匹配。如果不存在類型特定的版本,則會使用std中的版本(假定作用域中有using聲明)。

非常代細的讀者可能會奇怪為什么swap函數中的using聲明沒有隱藏HRasPtr版本swap的聲明。

在賦值運算符中使用swap

定義swap的類通常用swap來定義它們的賦值運算符。這些運算符使用了一種名為拷貝并交換(copy and swap)的技術。這種技術將左側運算對象與右側運算對象的一個副本進行交換:

//注意rhs是按值傳遞的,意味著HasPtr的拷貝構造函數
//將右側運算對象中的string拷貝到rhs
HasPtr& HasPtr::operator=(HasPtr rhs)
{
//交換左側運算對象和局部變量rhs的內部
swap(*this,rhs);//rhs現在指向本對象曾經使用的內存
return*this;//rhs被銷毀,從而delete了rhs中的指針
}

在這個版本的賦值運算符中,參數并不是一個引用,我們將右側運算對象以傳值方式傳遞給了賦值運算符。因此,rhs是右側運算對象的一個副本。參數傳遞時拷貝HasPtr的操作會分配該對象的string的一個新副本。

在賦值運算符的函數體中,我們調用swap來交換hs和this中的數據成員。這個調用將左側運算對象中原來保存的指針存入rhs中,并將zhs中原來的指針存入this中。因此,在swap調用之后,*this中的指針成員將指向新分配的string一一右側運算對象中string的一個副本。

當賦值運算符結束時,rhs被銷毀,HasPtr的析構函數將執行。此析構函數delete rhs現在指向的內存,即,釋放掉左側運算對象中原來的內存。

這個技術的有趣之處是它自動處理了自賦值情況且天然就是異常安全的。它通過在改變左側運算對象之前拷貝右側運算對象保證了自賦值的正確,這與我們在原來的賦值運算符中使用的方法是一致的。它保證異常安全的方法也與原來的賦值運算符實現一樣。代碼中唯一可能拋出異常的是拷貝構造函數中的new表達式。如果真發生了異常,它也會在我們改變左側運算對象之前發生。

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

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

相關文章

FFmpeg-chapter7和chapter8-使用 FFmpeg 解碼視頻(原理篇和實站篇)

解碼流程如下圖 流程:首先,通過 avcodec_alloc_context3(nullptr) 分配一個 AVCodecContext 結構體,然后使用 avcodec_parameters_to_context 將參數復制到上下文中,接著通過 avcodec_find_decoder 查找指定的解碼器,并…

2025 docker安裝TiDB數據庫

1.確保安裝了docker和docker-compose sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-composesudo chmod x /usr/local/bin/docker-compose2.編寫 Docker Compose 文…

定制化開發的WooCommerce獨立站商城更安全

定制化開發的WooCommerce獨立站商城在安全性、交易風險控制以及整體用戶體驗方面有顯著優勢。以下是定制化開發在這些方面的具體表現: 1. 安全性更高 定制化開發允許開發者從底層架構開始設計和優化,確保網站的安全性。以下是具體表現: (1…

CSS【實戰】模擬 html 的 title 屬性(鼠標懸浮顯示提示文字)

效果 原理 提示內容的定位&#xff1a;子絕父相鼠標懸浮前&#xff0c;提示內容visibility: hidden;通過 :hover 觸發鼠標懸浮樣式&#xff0c;提示內容變為 visibility: visible; 代碼 <!DOCTYPE html> <html lang"en"><head><meta charset&qu…

K8s控制器Deployment詳解

回顧 ReplicaSet 控制器,該控制器是用來維護集群中運行的 Pod 數量的&#xff0c;但是往往在實際操作的時候&#xff0c;我們反而不會去直接使用 RS&#xff0c;而是會使用更上層的控制器&#xff0c;比如說 Deployment。 Deployment 一個非常重要的功能就是實現了 Pod 的滾動…

【MYSQL數據庫異常處理】執行SQL語句報超時異常

MYSQL執行SQL語句異常&#xff1a;The last packet successfully received from the server was 100,107 milliseconds ago. The last packet sent successfully to the server was 100,101 milliseconds ago. 這個錯誤表明 MySQL 服務器與 JDBC 連接之間的通信超時了。通常由…

HJ C++11 Day2

Initializer Lists 對于一個類P class P{P(int a, int b){cout << "P(int, int), a" << a << ", b " << b << endl;}P(initializer_list<int> initlist){cout << "P(initializer_list<int>), val…

樹莓派5首次開機保姆級教程(無顯示器通過VNC連接樹莓派桌面)

第一次開機詳細步驟 步驟一&#xff1a;樹莓派系統燒錄1 搜索打開燒錄軟件“Raspberry Pi Imager”2 選擇合適的設備、系統、SD卡3 燒錄配置選項 步驟二&#xff1a;SSH遠程樹莓派1 樹莓派插電2 網絡連接&#xff08;有線或無線&#xff09;3 確定樹莓派IP地址 步驟三&#xff…

裝飾器模式--RequestWrapper、請求流request無法被重復讀取

目錄 前言一、場景二、原因分析三、解決四、更多 前言 曾經遇見這么一段代碼&#xff0c;能看出來是把request又重新包裝了一下&#xff0c;核心信息都不會改變 后面了解到這叫 裝飾器模式&#xff08;Decorator Pattern&#xff09; &#xff1a;也稱為包裝模式(Wrapper Pat…

大語言模型進化論:從達爾文到AI的啟示與展望

文章大綱 引言大語言模型中的“進化論”思想體現遺傳變異過度繁殖和生存斗爭大模型“過度繁殖”與“生存競爭”機制解析**一、過度繁殖:技術迭代的指數級爆發****二、生存競爭:計算資源的達爾文戰場****三、生存競爭勝出關鍵要素****四、行業競爭格局演化趨勢**核心結論自然選…

監聽 RabbitMQ 延時交換機的消息數、OpenFeign 路徑參數傳入斜杠無法正確轉義

背景 【MQ】一套為海量消息和高并發熱點消息&#xff0c;提供高可用精準延時服務的解決方案 我現在有一個需求&#xff0c;就是監聽 RabbitMQ 一個延時交換機的消息數&#xff0c;而 RabbitTemplate 是不存在對應的方法來獲取的。 而我們在 RabbitMQ 的控制臺卻可以發現延時交…

分布式網絡

分布式網絡&#xff08;Distributed Network&#xff09;指的是一種計算機網絡架構&#xff0c;其中計算資源&#xff08;計算、存儲、數據處理等&#xff09;分布在多個物理或邏輯上的節點上&#xff0c;而不是集中在單一的服務器或數據中心中。這種架構的主要目標是提高系統的…

【Elasticsearch】Elasticsearch 中使用 HDFS 存儲快照

在 Elasticsearch 中使用 HDFS 存儲快照的步驟如下&#xff1a; 1.安裝 HDFS 插件 要使用 HDFS 存儲 Elasticsearch 的索引快照&#xff0c;需要在 Elasticsearch 集群的所有節點上安裝 HDFS 插件。 ? 在線安裝&#xff1a;適用于網絡環境良好的場景&#xff0c;執行以下命…

淺談 DeepSeek 對 DBA 的影響

引言&#xff1a; 在人工智能技術飛速發展的背景下&#xff0c;DeepSeek 作為一款基于混合專家模型&#xff08;MoE&#xff09;和強化學習技術的大語言模型&#xff0c;正在重塑傳統數據庫管理&#xff08;DBA&#xff09;的工作模式。通過結合其強大的自然語言處理能力、推理…

STM32F4 UDP組播通信:填一填ST官方HAL庫的坑

先說寫作本文的原因&#xff0c;由于開項目開發中需要用到UDP組播接收的功能&#xff0c;但是ST官方沒有提供合適的參考&#xff0c;使用STM32CubeMX生成的代碼也是不能直接使用的&#xff0c;而我在網上找了一大圈&#xff0c;也沒有一個能夠直接解決的方案&#xff0c;deepse…

leetcode日記(85)驗證二叉搜索樹

不難&#xff0c;有兩種解法&#xff08;看答案才想到中序遍歷&#xff09;。 我用的是普通遞歸&#xff0c;和上一題差不多&#xff0c;規定min和max&#xff0c;每次遍歷縮小范圍: /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNod…

如何在rust中解析 windows 的 lnk文件(快捷方式)

一、從標題二開始看&#x1f601; 這些天在使用rust寫一個pc端應用程序&#xff0c;需要解析lnk文件獲取lnk的圖標以及原程序地址&#xff0c;之前并沒有過pc端應用程序開發的經驗&#xff0c; 所以在廣大的互聯網上游蕩了兩天。額&#x1f97a; 今天找到了這個庫 lnk_parse很…

啟動wsl里的Ubuntu24報錯:當前計算機配置不支持 WSL2,HCS_E_HYPERV_NOT_INSTALLED

問題&#xff1a;啟動wsl里的Ubuntu24報錯 報錯信息&#xff1a; 當前計算機配置不支持 WSL2。 請啟用“虛擬機平臺”可選組件&#xff0c;并確保在 BIOS 中啟用虛擬化。 通過運行以下命令啟用“虛擬機平臺”: wsl.exe --install --no-distribution 有關信息&#xff0c;請訪…

Python使用FastAPI結合Word2vec來向量化200維的語言向量數值

準備 pip install fastapi>0.68.0 pip install uvicorn[standard]>0.15.0 pip install gensim>4.0.0 pip install jieba>0.42.1 pip install numpy>1.21.0 pip install scikit-learn>1.0.0少了的就直接補充就好 代碼 from fastapi import FastAPI, HTTPExc…

ControlNet

文章目錄 摘要abstract1.ControlNet1.1 原文摘要1.2 模型架構1.3 架構細節1.4 訓練損失函數1.5 實驗1.6 結論 2.總結參考文獻 摘要 本周學習的ControlNet 是一種用于文本到圖像擴散模型&#xff08;如 Stable Diffusion&#xff09;的條件控制方法。它通過凍結預訓練的擴散模型…