C++11QT復習 (四)

Day6-1 輸入輸出流運算符重載(2025.03.25)

1. 拷貝構造函數的調用時機
2. 友元2.1 友元函數
3. 輸入輸出流運算符重載3.1 關鍵知識點3.2 代碼3.3 關鍵問題3.4 完整代碼
4. 下標訪問運算符 `operator[]`4.1 關鍵知識點4.2 代碼
5. 函數調用運算符 `operator()`5.1 關鍵知識點5.2 代碼5.3 示例5.4 完整代碼
6. 總結

1. 回顧

1.1 拷貝構造函數的調用時機

拷貝構造函數在以下情況會被調用:

  1. 對象初始化

    • 當用一個已經存在的對象去初始化一個剛剛創建的對象時,調用拷貝構造函數。
    • 例:
      Complex c1(1, 2);
      Complex c2 = c1; // 調用拷貝構造函數
      
  2. 函數參數傳遞

    • 當形參與實參都是對象時,在函數調用時會調用拷貝構造函數。
    • 例:
      void func(Complex c) { }
      func(c1); // 形參與實參結合時調用拷貝構造函數
      
  3. 函數返回對象

    • 當函數返回一個對象時,可能調用拷貝構造函數(但現代 C++ 編譯器會嘗試優化此過程,如返回值優化 RVO)。
    • 例:
      Complex func() {Complex c(3, 4);return c; // 可能調用拷貝構造函數
      }
      

2. 友元

2.1 友元函數
  • 友元函數可以訪問類的私有成員。
  • 友元聲明可以出現在類的 publicprotectedprivate 部分,不影響其權限。
  • 友元關系是單向的、不可傳遞的
    • 單向:如果 AB 的友元,B 并不會自動成為 A 的友元。
    • 不可傳遞:如果 AB 的友元,BC 的友元,A 并不會自動成為 C 的友元。

3. 輸入輸出流運算符重載

3.1 關鍵知識點
  1. operator<< 必須是友元函數

    • 由于 cout << c1; 左操作數是 std::ostream,不能修改 std::ostream,所以 operator<< 不能是 Complex 的成員函數。
  2. operator>> 不能是 const 成員函數

    • 因為 operator>> 需要修改對象的值,因此不能加 const
3.2 代碼
std::ostream& operator<<(std::ostream& os, const Complex& rhs) {if (rhs._imag > 0) {os << rhs._real << " + " << rhs._imag << "i";} else if (rhs._imag == 0) {os << rhs._real;} else {os << rhs._real << " - " << -rhs._imag << "i";}return os;
}std::istream& operator>>(std::istream& is, Complex& rhs) {std::cout << "請輸入復數的實部和虛部:" << std::endl;is >> rhs._real >> rhs._imag;return is;
}
3.3 關鍵問題
  • 為什么 operator<<operator>> 的返回值是 std::ostream&std::istream&

    • 這樣可以實現連續輸入輸出
      cout << c1 << c2 << endl;  // 連續輸出
      cin >> c1 >> c2;           // 連續輸入
      
  • 為什么 operator<<ostream& 參數不能去掉 &

    • 因為 ostream 的拷貝構造函數已被 delete,不能復制 ostream 對象。
3.4 完整代碼
#include <iostream>
#include <limits.h>
#include <ostream>using namespace std;//復數
class Complex
{friend Complex operator*(const Complex& lhs, const Complex& rhs);//Complex operator/(const Complex& rhs) const;
public:Complex(double r = 0, double i = 0): _real(r), _imag(i){cout << "Complex(double r = 0, double i = 0)" << endl;}~Complex(){cout << "~Complex()" << endl;}void display() const{if (_imag > 0){cout << _real << " + " << _imag << "i" << endl;}else if (_imag == 0){cout << _real << endl;}else{cout << _real << " - " << -_imag << "i" << endl;}}//成員函數,operator輸出運算符重載 //cout << c1 << endl; //第一個參數cout ,第二個參數c1//std::ostream& operator<<(std::ostream& os, const Complex& rhs); //error!隱含this指針//對于輸出流運算符函數而言,不能寫成成員函數的形式,因為違背了運算符重載的原則,不能改變操作數的順序//std::ostream& operator<<(std::ostream& os);//error!this指針在參數列表的第一個位置/*友元函數可以放在類的 任何位置(public / protected / private),不影響其功能。從代碼規范角度,建議統一放在類定義的開頭或結尾,以提高可讀性。友元關系是單向的,且不具備傳遞性(即類 A 的友元函數不會自動成為類 B 的友元)。*/friend std::ostream& operator<<(std::ostream& os, const Complex& rhs);//輸入流運算符重載friend std::istream& operator>>(std::istream& is, Complex& rhs);private:double _real;double _imag;
};
//問題1 :參數列表中ostrream的引用符號&能不能去掉?
//解答1 :不能去掉因為形參"os"和實參"cout"結合的時候會滿足拷貝構造函數的調用時機,但是ostream中的拷貝構造函數已被delete//問題2 : 函數返回類型中的引用符號&能不能刪除?
//解答2 : 不能取掉,因為return os,返回類型滿足拷貝構造函數的調用時機3//注釋:basic_ifstream( const basic_ifstream& rhs ) = delete; (7)	(since C++11)
//	   basic_ofstream( const basic_ofstream& rhs ) = delete; (7)	(since C++11)
//ifstram 和 ofstream 的拷貝構造函數已經從C++11開始刪除了
std::ostream& operator<<(std::ostream& os, const Complex& rhs)
{cout << "std::ostream& operator<<(std::ostream& os, const Complex& rhs)" << endl;if (rhs._imag > 0){os << rhs._real << " + " << rhs._imag << "i" << endl;}else if (rhs._imag == 0){os << rhs._real << endl;}else{os << rhs._real << " - " << -rhs._imag << "i" << endl;}return os;
}void readDouble(std::istream& is, double& rhs)
{while (is >> rhs, !is.eof()){if (is.bad()){std::cerr << "istream is bad" << endl;return;}else if (is.fail()){is.clear();//重置流的狀態is.ignore(std::numeric_limits<::std::streamsize>::max(), '\n');//清空緩沖區cout << "請注意:需要輸入double類型的數據!";}else{cout << "rhs = " << rhs << endl;break;}}
}//輸入流運算符重載
std::istream& operator>>(std::istream& is, Complex& rhs)//因為要修改rhs 的值,所以不能加const
{cout << "std::istream& operator>>(std::istream& is, Complex& rhs)" << endl;cout << "請分別輸入復數的實部和虛部:" << endl;//is >> rhs._real >> rhs._imag;readDouble(is, rhs._real);readDouble(is, rhs._imag);return is;
}void testOutputOperator()
{Complex c1(1, 2);cout << "c1 = ";c1.display();cout << endl << endl;//cout << "c1 = " << c1.display();//為什么沒有做輸出流運算符重載之前上面的寫法 不可行呢? 解答:二元“<<”: 沒有找到接受“void”類型的右操作數的運算符(或沒有可接受的轉換)cout << "c1 = " << c1 << endl;cout << endl;Complex c2;cin >> c2;cout << "c2 = " << c2 << endl;
}int main(int argc, char* argv[])
{testOutputOperator();return 0;
}

4. 下標訪問運算符 operator[]

4.1 關鍵知識點
  • operator[] 主要用于自定義數組類型,使得 obj[idx] 訪問數組元素。
  • 返回值應為 T&,以保證能夠修改數組內容。
  • 必須進行越界檢查,以防止訪問非法內存。
4.2 代碼
char& CharArrar::operator[](size_t idx) {if (idx < _size) {return _data[idx];} else {static char charNull = '\0';return charNull;}
}

5. 函數調用運算符 operator()

5.1 關鍵知識點
  • 使對象像函數一樣調用,稱為仿函數(函數對象)
  • 可存儲狀態,例如調用次數。
5.2 代碼
class FunctionObject {
public:int operator()(int x, int y) {++_cnt;return x + y;}int operator()(int x, int y, int z) {++_cnt;return x * y * z;}private:int _cnt = 0;
};
5.3 示例
FunctionObject fo;
cout << "fo(3, 4) = " << fo(3, 4) << endl;
cout << "fo(3, 4, 5) = " << fo(3, 4, 5) << endl;
5.4 完整代碼
//bracket.h
#pragma once
#include <iostream>
#include <string.h>using namespace std;
class CharArrar
{
public:CharArrar(size_t sz = 10):_size(sz), _data(new char[_size]){cout << " CharArrar(size_t sz = 10)" << endl;}~CharArrar(){cout << "~CharArrar()" << endl;if (_data){delete _data;_data = nullptr;}}size_t size() const{return _size;}//下標訪問運算符的重載//int arr[10] = { 1,2,3,4,5 };//arr.operator[](idx);char& operator[](size_t idx);
private:size_t _size;char* _data;
};

//bracket.cpp
#include "bracket.h"/*
要修復錯誤 C2106: “=”: 左操作數必須為左值,需要確保 CharArrar 類的
下標運算符 operator[] 返回一個可修改的左值。當前的 operator[] 聲明
返回的是一個 char,這不是一個可修改的左值。我們需要將其修改為返回一個 char&。
*/
char& CharArrar::operator[](size_t idx)
{if (idx < _size){return _data[idx];}else{static char charNUll = '\0';//靜態變量延長生命周期return charNUll;   //使得返回的實體生命周期比函數的生命周期長}
}//C++中優勢:重載的下標訪問運算符考慮了越界的問題
//引用什么時候需要加上?
//1.如果返回類型是類型的時候,可以減少拷貝構造函數的執行
//2.有可能需要一個左值(來調用右操作數),而不是拷貝后的右值、
//3.cout << "111" << c1 << endl << 10 << 1 << endl,像這種情況下連續使用的時候可以加上引用 

//parenthese.h
#pragma once
#include <iostream>using namespace std;class FunctionObject
{
public:int operator()(int x, int y);int operator()(int x, int y, int z);private:int _cnt;//記錄被調用的次數(函數對象狀態)
};

parenthese.cpp
#include <iostream>
#include "parenthese.h"using namespace std;int FunctionObject::operator()(int x, int y)
{++_cnt;cout << "int operator()(int x, int y)" << endl;return x + y;
}int FunctionObject::operator()(int x, int y, int z)
{cout << "int operator()(int x, int y,int z)" << endl;++_cnt;return x * y * z;
}

//main.cpp
#include <iostream>
#include "bracket.h"
#include "parenthese.h"using namespace std;int add(int x, int y)
{cout << "int add(int x,int y)" << endl;static int cnt = 0;++cnt;return x + y;
}void testFunctionObject()
{FunctionObject fo;int a = 3;int b = 4;int c = 5;//fo本質上是一個對象,但是他的使用cout << "fo(a,b) = " << fo(a, b) << endl;cout << "fo(a, b ,c) = " << fo(a, b, c) << endl;cout << endl;//正經的函數cout << "add(a, b) = " << add(a, b) << endl;
}void testCharArrar()
{//把字符串中的內容拷貝到CharArrayconst  char* pstr = "hello cpp";CharArrar ca(strlen(pstr) + 1);for(size_t idx = 0; idx != ca.size(); ++idx){//ca[idx] = pstr[idx];//上面和下面兩條代碼等價ca.operator[](idx) = pstr[idx];}for (size_t idx = 0; idx != ca.size(); ++idx){cout << ca[idx] << "  ";}cout << endl;
}int main(int argc, char** argv)
{testFunctionObject();testCharArrar();return  0;
}

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

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

相關文章

數倉架構告別「補丁」時代!全新批流一體 Domino 架構終結“批流縫合”

在數字化轉型的浪潮中&#xff0c;企業對數據處理的需求日益復雜多變&#xff0c;傳統的批處理和流處理架構已難以滿足日益增長的性能和時效性要求。在此背景下&#xff0c;YMatrix CEO 姚延棟發布了深度文章《數倉架構告別「補丁」時代&#xff01;全新批流一體 Domino 架構終…

一文詳解QT環境搭建:ubuntu20.4安裝配置Qt5

隨著軟件開發技術的不斷進步&#xff0c;跨平臺應用程序的需求日益增長&#xff0c;開發者們面臨著如何在不同操作系統之間保持代碼的一致性和效率的問題。Qt作為一個成熟的跨平臺C框架&#xff0c;在這方面提供了卓越的支持&#xff0c;不僅簡化了GUI應用程序的創建過程&#…

安全+低碳+高效:Acrel-3000助力企業打造未來型電能管理體系-安科瑞黃安南

一 背景 電能因為方便傳輸、易于轉換、便于控制等特性&#xff0c;成為廣大企事業單位生產、辦公最主要的能量來源。雙碳背景下&#xff0c;由于電能清潔、高效、零排放的特點&#xff0c;能源消費側將逐步以電代煤、以電代油、以電代氣&#xff0c;形成以電為中心的能源消費體…

Docker 安裝 RabbitMQ

以下是在Docker中安裝RabbitMQ并實現配置、數據、日志文件映射的完整步驟。 步驟 1&#xff1a;創建本地目錄結構 # 創建配置、數據、日志目錄 mkdir -p /root/docker/rabbitmq/{conf,data,logs}# 目錄結構說明&#xff1a; # - conf: 存放自定義配置文件 # - data: 持久化存儲…

SAP-ABAP:SAP數據集成全場景技術指南(BAPI、RFC、IDOC、BATCHJOB、ODATA、WEBSERVICE):從實時交互到批量處理

SAP數據集成全場景技術指南:從實時交互到批量處理 #mermaid-svg-hpPMerJYUerla0BJ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-hpPMerJYUerla0BJ .error-icon{fill:#552222;}#mermaid-svg-hpPMerJYUerla0BJ .er…

運維規則之總結(Summary of Operation and Maintenance Rules)

運維規則之總結 在運維領域&#xff0c;經驗和流程往往決定了系統的穩定性與可靠性。一個運維人&#xff0c;總結出了以下10條運維規則&#xff0c;涵蓋了從基礎管理到高級策略的全面內容&#xff0c;旨在幫助運維人員更好地應對各種挑戰&#xff0c;確保系統的平穩運行。 1.…

⑦(ACG-網絡配置)

網絡配置是指對計算機網絡的各種參數進行設置和調整&#xff0c;以實現網絡正常運行和高效通信。網絡配置包括多方面的內容&#xff0c;常見的配置包括&#xff1a; 1. IP地址設置&#xff1a;IP地址是設備在網絡中的身份標識&#xff0c;設置IP地址是網絡配置的基礎&#xff…

Redis學習二

Redis和數據庫數據一致性問題 Redis作為緩存分兩種情形 只讀緩存, 只讀緩存無需考慮數據更新問題, Redis中有則返回Redis中的數據, Redis無則查詢數據庫讀寫緩存 同步直寫策略異步緩寫策略 數據讀取流程: 正常回寫Redis代碼流程: public Object getDataById(String id) {…

深入理解 Linux 文件權限:從 ACL 到擴展屬性,解剖底層技術細節與命令應用

Linux 以其強大而精密的文件權限和屬性管理機制著稱&#xff0c;這一體系不僅是系統安全的關鍵基石&#xff0c;還為靈活性和擴展性提供了堅實支撐。從傳統的九位權限模型到訪問控制列表&#xff08;ACL&#xff09;、擴展文件屬性&#xff08;Extended Attributes&#xff09;…

劍指Offer35- - 鏈表

1. 題目描述 這題題意感覺說的不是很清楚&#xff0c;容易讓人產生歧義&#xff01;其實題意很簡單&#xff0c;給你一個鏈表 head&#xff0c;你深拷貝它&#xff0c;然后返回即可&#xff0c;注意不能修改原鏈表 /* // Definition for a Node. class Node { public:int val;N…

C 語言常用關鍵字詳解:static、const、volatile

C 語言常用關鍵字詳解&#xff1a;static、const、volatile 文章目錄 C 語言常用關鍵字詳解&#xff1a;static、const、volatile1. static 關鍵字1.1 用于局部變量示例&#xff1a; 1.2 用于全局變量示例&#xff1a; 1.3 用于函數示例&#xff1a; 2. const 關鍵字2.1 用于局…

Centos7本地部署阿里Qwen2-7B模型

1.從hagging face下載模型 2.把下載的模型文件&#xff0c;放到/usr/local/Qwen2-7B目錄下 3.創建虛擬環境&#xff0c;安裝依賴 1.環境安裝 sudo yum update -y sudo yum install -y python3 python3-pip git 2.創建虛擬環境并激活 python3 -m venv qwen2_env source qwen2_…

群暉監控套件通過ONVIF協議添加海康攝像頭

1. 首先登錄錄像機 通道管理 找到每個攝像頭的IP地址 2. 登錄某個攝像頭 配置 3. 添加用戶名&#xff08;注意不能是admin&#xff09; 設置賬戶密碼 用戶類型選管理員 4. 群暉里面添加攝像頭&#xff0c;自動搜索&#xff0c;添加剛剛那個IP的攝像頭 5. 驗證…

【C++】 —— 筆試刷題day_8

一、求最小公倍數 題目解析 題目很簡單&#xff0c;給定兩個數a和b求它們的最小公倍數。 算法思路 對于求兩個數的最小公倍數問題&#xff0c;想必已經非常熟悉了&#xff1b; 在之前學校上課時&#xff0c;記得老師提起過&#xff0c;最小公倍數 兩個數的乘積 除以最大公約數…

MTK Android12-Android13 設置系統默認語言

Android 系統&#xff0c;默認語言 文章目錄 需求&#xff1a;場景 參考資料實現方案實現思路編譯腳本熟悉-平臺熟悉mssi_64_cnkernel-4.19 解決方案修改文件-實現方案 源碼分析PRODUCT_LOCALES 引用PRODUCT_DEFAULT_LOCALE 定義get-default-product-locale 方法定義PRODUCT_DE…

系統如何查找文件?inode號又是什么?

下面分別詳細解釋您提到的三個問題&#xff1a; “文件系統怎么定位文件”、“inode 是什么”、“為什么刪除后還可能被占用”。 一、文件系統怎么定位文件 1.1 目錄與文件名并不直接存儲文件數據 在常見的 Unix/Linux 文件系統&#xff08;如 ext4、xfs&#xff09;或類似的…

05-SpringBoot3入門-整合SpringMVC(配置靜態資源、攔截器)

1、說明 在01-SpringBoot3入門-第一個項目-CSDN博客中&#xff0c;其實就已經整合了SpringMVC。下面講解怎么配置靜態資源和攔截器 2、配置靜態資源 命名&#xff1a;static&#xff08;文件夾&#xff09; 位置&#xff1a;src/main/resources 編寫一個html文件 訪問 http:/…

Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多變量回歸預測

聚劃算&#xff01;Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多變量回歸預測 目錄 聚劃算&#xff01;Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多變量回歸預測預測效果基本介紹程序設計參考資料 預測效果 基本介紹 聚劃算&#xff01;Tran…

樹莓派瀏覽器配置全解析:從輕量系統到網頁應用平臺

樹莓派&#xff08;Raspberry Pi&#xff09;不僅是嵌入式開發的入門利器&#xff0c;也因其低成本和強大的社區支持而成為物聯網、數字標牌、教育培訓等領域的熱門平臺。在很多應用中&#xff0c;運行一個瀏覽器并作為 Web 前端展示、操作或交互的能力顯得尤為關鍵。 但在資源…

初識Qt(一)

本文部分ppt、視頻截圖原鏈接&#xff1a;萌馬工作室的個人空間-萌馬工作室個人主頁-嗶哩嗶哩視頻 1. Qt是什么&#xff1f; Qt是一個跨平臺的C應用程序開發框架&#xff0c;它既為圖形用戶界面(GUI)程序開發提供了強大支持&#xff0c;也能用于開發非GUI的控制臺程序、服務端…