【C++】類和對象(4)

目錄

1. 類型轉換

非explicit的單參數構造函數

示例?

explicit的單參數構造函數

示例

不同版本的行為?

示例 (單參數)

示例(多參數且其余參數有默認值 )

?示例(多參數且無默認值)

2. static成員變量

例題1

例題2?

3. 友元

4. 內部類

例題

5. 匿名對象

示例?


1. 類型轉換

C++支持將內置類型隱式轉換為類類型對象,需要非explicit的單參數構造函數或者多參數但其余參數有默認值的構造函數

非explicit的單參數構造函數

示例?

假設定義一個 Length ?類來表示長度,單位是米,它有一個接受 int ?類型參數的構造函數用于設置長度數值:

#include <iostream>
using namespace std;class Length {
public:// 接受int類型參數的構造函數Length(int meter) : length(meter){}void show() const {cout << "長度為: " << length << " 米" << endl;}
private:int length;
};int main() { // 復制初始化,將int類型的5轉換為Length類型的對象lenLength len = 5; //想要創建一個表示長度為5米的length對象len len.show();return 0;
}

這里 int ?類型的 5 ?可以通過 Length ?類的構造函數隱式轉換為 Length ?類型的對象,使用 Length len = 5; ?這種復制初始化的方式,代碼看起來就像是把一個整數值賦給了 Length ?類型的對象,比較符合常規的賦值思維習慣,簡單易懂。相比之下,直接初始化 Length len(5); ?雖然功能一樣,但復制初始化在這種類型轉換很自然的場景里,從寫法上會給人一種更直觀的感覺。

explicit的單參數構造函數

示例

如果構造函數被聲明為explicit ,就不再支持隱式類型轉換,僅允許直接初始化

#include <iostream>
using namespace std;class MyClass 
{
public:    explicit MyClass(int value) // explicit 修飾構造函數,禁止隱式轉換: data(value) {}   
private:int data;
};int main() 
{MyClass obj(10);// 正確,顯式調用構造函數   // MyClass obj = 20; 錯誤! 因為 MyClass 的構造函數是 explicit,禁止隱式轉換return 0;
}

不同版本的行為?

  • C++98/03Length len = 5; 復制初始化的處理方式是:先使用5調用構造函數 Length(int meter) 生成一個臨時的Length類型的對象,然后再通過拷貝構造函數將臨時對象復制到 len。允許編譯器優化省略。
  • C++11優化為直接構造。編譯器必須省略臨時對象和拷貝構造函數的調用,Length len = 5; 編譯器會直接調用構造函數初始化 len,提高了程序效率。

示例 (單參數)

class MyClass
{
public:MyClass(int value) // 非 explicit,允許隱式轉換: data(value){} // 無拷貝構造函數(編譯器會默認生成)
private:int data;
};void func(MyClass obj)
{/* ... */
}int main()
{MyClass obj = 10;  // 隱式轉換:int → MyClass//C++98/03行為:先調用構造函數生成臨時的MyClass類型對象,再通過拷貝構造函數(若用戶未定義,編譯器默認生成一個公有的)將臨時對象賦值給obj(這里臨時對象生命周期僅用于初始化obj,拷貝完成后銷毀)//C++11及以后行為:允許直接構造obj而不生成臨時對象,不調用拷貝構造函數func(20);   // 隱式轉換:int → MyClass//C++98/03行為:(兩次開銷)先生成臨時的MyClass類型對象,再通過拷貝構造(若用戶未定義,編譯器默認生成一個公有的)函數將臨時對象傳遞給func函數的形參obj//C++11及以后行為:(一次開銷)允許直接在函數func的形參obj的存儲位置(函數棧幀)構造MyClass對象,而不生成臨時對象return 0;
}
class MyClass
{
public:MyClass(int value) // 非 explicit,允許隱式轉換: data(value){}// 無拷貝構造函數(編譯器會默認生成)
private:int data;
};void func(const MyClass& a)//參數為引用類型
{/* ... */
}int main()
{MyClass obj = 10;  func(20);  //不管是C++98/03還是C++11及以后,當參數是引用類型時,必須創建臨時對象,然后綁定到引用上,臨時對象生命周期延長至函數結束return 0;
}

示例(多參數且其余參數有默認值 )

#include <iostream>
using namespace std;
class A 
{
public:// 多參數但其余參數有默認值的構造函數A(int a, int b = 4) : value(a + b) {cout << value << endl;}private:int value;
};int main() 
{        A obj2 = 10;  //這里 10 是int類型,隱式轉換為A類型,調用多參數(有默認值)構造函數return 0;
}

?示例(多參數且無默認值)

#include <iostream>
using namespace std;
class B
{
public:// 多參數且無默認值的構造函數B(int a, int b) : num(a + b) {cout << num << endl;}private:int num;
};int main() 
{// 不能隱式轉換,顯式調用構造函數進行類型轉換B obj = B(3, 5);//理論上B(3, 5)復制初始化 先調用構造函數創建臨時對象,再調用拷貝構造函數將臨時對象賦值給obj,實際上進行了優化//不產生臨時對象,也不調用拷貝構造函數,等價于直接初始化 B obj(3,5),二者在效果和性能上完全一致return 0;
}
#include <iostream>
using namespace std;
class Point 
{
public:Point(int x, int y) : m_x(x), m_y(y) {}void print() const {std::cout << "(" << m_x << ", " << m_y << ")";}private:int m_x;int m_y;
};void displayPoint(const Point& p)
{p.print();std::cout << std::endl;
}int main() 
{// 1.顯式構造Point對象后傳遞displayPoint(Point(10, 20));//Point(10, 20)調用構造函數創建臨時對象,這個臨時對象直接綁定到函數的參數p上// 2.使用列表初始化語法,編譯器隱式調用構造函數displayPoint({ 30, 40 });//編譯器看到這種形式,知道接受一個Point類型引用參數,Point類有構造函數,此時編譯器會隱式地使用{10,20}作為參數調用構造函數,創建臨時對象//3.直接初始化然后再傳Point p(5, 5);displayPoint(p);return 0;
}

2. static成員變量

  • 用static修飾的成員變量,稱之為靜態成員變量,靜態成員變量必須在在類外進行初始化。
  • 所有對象共享同一份靜態成員變量,它存儲在全局數據區,而不是在每個對象的內存空間中。
  • 用static修飾的成員函數,稱之為靜態成員函數,靜態成員函數沒有this指針
  • 靜態成員函數中可以訪問其他的靜態成員,但是不能訪問非靜態成員,因為沒有this指針。
  • 非靜態的成員函數,可以訪問任意的靜態成員變量和靜態成員函數。
  • 突破類域就可以訪問靜態成員,可以通過類名::靜態成員或者對象.靜態成員來訪問靜態成員變量和靜態成員函數。
  • 靜態成員也是類的成員,受public、protected、private訪問限定符的限制。
  • 靜態成員變量不能在聲明位置給缺省值初始化,因為缺省值是給構造函數初始化列表的,靜態成員變量不屬于某個對象,不走構造函數初始化列表。

示例:?

//實現一個類,計算程序中創建出了多少個類對象?
#include<iostream>
using namespace std;
class A
{
public:A(){++_scount;}A(const A& t){++_scount;}~A(){--_scount;}  //靜態成員函數static int GetACount(){//_a++; error! 靜態成員函數不能訪問非靜態成員,因為沒有this指針return _scount;}void func(){cout << _scount << endl;cout << GetACount() << endl;}
private:int _a = 4;//類里面聲明static int _scount;
};//類外?初始化
int A::_scount = 0;int main()
{cout << A::GetACount() << endl;A a1, a2;A a3(a1);cout << A::GetACount() << endl;cout << a1.GetACount() << endl;//cout << A::_scount << endl;  編譯報錯:error C2248 : “A::_scount” : ?法訪問private成員(在“A”類中聲明) 受訪問限定符限制!return 0;
}

例題1

?鏈接[?求1+2+3+……n]

?

class Sum
{
public:Sum(){_ret+=_i;++_i;}static int Getret(){return _ret;}
private:static int _i;static int _ret;};int Sum:: _i=1;int Sum:: _ret=0;class Solution {
public:int Sum_Solution(int n) {// Sum a[n]; 變長數組Sum*p=new Sum[n];return Sum::Getret();}
};

例題2?

設已經有A,B,C,D 4個類的定義,程序中A,B,C,D構造函數調用順序為(C->A->B->D

設已經有A,B,C,D 4個類的定義,程序中A,B,C,D析構函數調用順序為(B->A->D->C

C c;
int main()
{A a;B b;static D d;return 0;
}

解析:

變量c是全局對象,其構造函數最先調用;在main函數中先聲明A a; 然后聲明B b; 所以A的構造函數先于B調用;D d;是靜態局部對象,其構造函數在main函數首次執行到該聲明處調用,在A和B之后。所以順序為:C->A->B->D

非靜態局部對象a、b 析構時機是離開main函數作用域時,順序為構造的反序(B->A);靜態對象(全局c和靜態局部d)析構時機是程序結束時,順序為構造的反序(全局C先構造,靜態局部D后構造,故析構順序為D->C)。所以順序為B->A->D->C

3. 友元

一般來說,類的私有成員外部是不能訪問的,而友元函數在一些特殊場景下很有用,比如需要在類外部的函數中訪問類的私有數據進行特定操作,又不想把這些數據設為公有成員破壞封裝性,就可以將該函數聲明為友元函數。

  • 友元提供了一種突破類訪問限定符封裝的方式,友元分為:友元函數和友元類,在函數聲明或者類聲明的前面加 friend,并且把友元聲明放到一個類的里面。
  • 外部友元函數可訪問類的私有和保護成員,友元函數僅僅是一種聲明,他不是類的成員函數。
  • 友元函數不屬于類的成員函數,沒有 this ?指針。調用時和普通函數一樣,直接使用函數名調用。
  • 友元函數可以在類定義的任何地方聲明,不受類訪問限定符限制。
  • 友元類的關系是單向的,不具有交換性,比如A類是B類的友元,但是B類不是A類的友元。
  • 友元類關系不能傳遞,如果A是B的友元,B是C的友元,但是A不是C的友元。
  • 一個函數可以是多個類的友元函數。
  • 友元提供了便利,但是友元會增加耦合度,破壞了類的封裝性,所以友元不宜多用,使用時要謹慎。
#include<iostream>
using namespace std;class B; //前置聲明,否則編譯器在A類中編譯時會因為不知道B是什么類型而報錯,告訴編譯器B是一個類
class A
{//友元聲明
friend void func(const A& aa, const B& bb);
private:int _a1 = 1;int _a2 = 2;
};class B
{
//友元聲明
friend void func(const A& aa, const B& bb);
private:int _b1 = 3;int _b2 = 4;
};void func(const A& aa, const B& bb)//func函數可以是多個類的友元函數
{cout << aa._a1 << endl;cout << bb._b1 << endl;
}int main()
{A aa;B bb;func(aa, bb);return 0;
}

當一個類被聲明為另一個類的友元類時,那么這個類的所有成員函數都可以訪問另一個類的私有和保護成員,無需為每個成員函數單獨聲明friend關鍵字。?

#include<iostream>
using namespace std;class A
{//友元聲明
friend class B;// B整體是A的友元類,友元類的聲明是對整個類的授權,B類的所有成員函數都可訪問A的私有成員private:int _a1 = 1;int _a2 = 2;
};class B
{
public:void func1(const A& aa){cout << aa._a1 << endl;cout << _b1 << endl;}void func2(const A& aa){cout << aa._a2 << endl;cout << _b2 << endl;}private:int _b1 = 3;int _b2 = 4;
};int main()
{A aa;B bb;bb.func1(aa);bb.func2(aa);return 0;
}

4. 內部類

如果一個類定義在另一個類的內部,這個內部類就叫做內部類。內部類是一個獨立的類,跟定義在 全局相比,它只是受外部類類域限制和訪問限定符限制,所以外部類定義的對象中不包含內部類內部類默認是外部類的友元類。

#include<iostream>
using namespace std;class A
{
private:static int _k;int _h = 1;
public:class B  //B默認就是A的友元{public:void foo(const A& a){cout << _k << endl;          cout << a._h << endl;        }private:int _b = 1;};
};int A::_k = 1; //初始化靜態成員變量int main()
{cout << sizeof(A) << endl; //4 非靜態成員_hA::B b;//B類的作用域在A類內部,在外部使用B類時需要指定其所屬的外部類域A aa;b.foo(aa);return 0;
}

內部類本質也是一種封裝,當A類跟B類緊密關聯,A類實現出來主要就是給B類使用,那么可以考慮把A類設計為B的內部類,如果放到private/protected位置,那么A類就是B類的專屬內部類,其他地方都用不了。?

例題

?鏈接[?求1+2+3+……n]?

class Solution 
{
private:static int _i;static int _ret;class Sum //內部類{public:Sum(){_ret+=_i;++_i;}         };public:int Sum_Solution(int n) {Sum a[n];//變長數組// Sum*p=new Sum[n];// delete []p;return _ret;}};int Solution:: _i=1;int Solution:: _ret=0;

5. 匿名對象

類型 (實參)定義出來的對象叫做匿名對象,相比之前定義的類型 對象名(實參)定義出來的叫有名對象

匿名對象通常是臨時對象,它們在表達式結束后會被自動銷毀

示例?

#include<iostream>
using namespace std;class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};class Solution
{
public:int Sum_Solution(int n) {//...return n;}
};int main()
{//有名對象//A aa1(); 不能這么定義對象,因為編譯器無法識別這是是?個函數聲明,還是對象定義  A aa1;cout << endl;A aa2(2);cout << endl;  //匿名對象,匿名對象的特點不?取名字,但是它的?命周期只有這一行,我們可以看到下一行他就會自動調用析構函數A();cout << endl;A(1);cout << endl;//匿名對象在這樣場景下就很好?:/*Solution sl;cout << sl.Sum_Solution(10) << endl;*///為了更方便cout << Solution().Sum_Solution(10) << endl;return 0;
}

運行結果:

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

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

相關文章

蒼穹外賣10

WebSocket WebSocket是基于TCP的一種新的網絡協議。它實現了瀏覽器與服務器全雙工通信----瀏覽器和服務器只需要完成一次握手&#xff0c;兩者之間就可以創建持久性的連接&#xff0c;并進行雙向數據傳輸。 HTTP協議和WebSocket協議對比&#xff1a; HTTP是短鏈接 WebSocke…

STM32的Flash映射雙重機制

在STM32微控制器中&#xff0c;存在一個重要的內存映射特性&#xff1a;Flash存儲器可以同時出現在兩個不同的地址區域&#xff0c;而且可以通過重映射功能改變CPU啟動時從哪個地址獲取初始指令。 STM32的Flash映射雙重機制 當描述"通常起始于地址0x00000000&#xff0c…

在 Spring Boot 中實現異常處理的全面指南

在現代 Web 應用開發中&#xff0c;異常處理是確保系統健壯性和用戶體驗的關鍵環節。Spring Boot 作為一個功能強大的 Java 框架&#xff0c;提供了靈活的異常處理機制&#xff0c;能夠統一管理應用程序中的錯誤&#xff0c;提升代碼可維護性和響應一致性。2025 年&#xff0c;…

學習記錄:DAY19

Docker 部署與項目需求分析 前言 人總是本能地恐懼未知&#xff0c;令生活陷入到經驗主義的循環之中。但我們終將面對。今天的目標是把 Docker 部署學完&#xff0c;然后對項目進行需求分析。 日程 下午 4:30&#xff1a;Docker 部署項目部分學完了&#xff0c;做下筆記。晚…

Jackson 使用方法詳解

Jackson 是 Java 生態中最流行的 JSON 處理庫&#xff0c;也是 Spring Boot 的默認 JSON 解析器。它提供了高性能的 JSON 序列化&#xff08;對象 → JSON&#xff09;和反序列化&#xff08;JSON → 對象&#xff09;功能。以下是 Jackson 的全面使用指南。 1. 基礎依賴 Mave…

【網絡入侵檢測】基于源碼分析Suricata的統計模塊

【作者主頁】只道當時是尋常 【專欄介紹】Suricata入侵檢測。專注網絡、主機安全,歡迎關注與評論。 1. 概要 ?? 在 Suricata 的配置文件中,stats 節點用于配置統計信息相關的參數,它的主要作用是控制 Suricata 如何收集和輸出統計數據,幫助用戶了解 Suricata 的運行狀態和…

回歸預測 | Matlab實現DBO-LightGBM蜣螂算法優化輕量級梯度提升機多輸入單輸出回歸預測,作者:機器學習之心

回歸預測 | Matlab實現DBO-LightGBM蜣螂算法優化輕量級梯度提升機多輸入單輸出回歸預測&#xff0c;作者&#xff1a;機器學習之心 目錄 回歸預測 | Matlab實現DBO-LightGBM蜣螂算法優化輕量級梯度提升機多輸入單輸出回歸預測&#xff0c;作者&#xff1a;機器學習之心預測效果…

風力發電領域canopen轉Profinet網關的應用

在風力發電領域&#xff0c;開疆canopen轉Profinet網關KJ-PNG-205的應用案例通常涉及將風力渦輪機內部的CANopen網絡與外部的Profinet工業以太網連接起來。這種轉換網關允許風力發電場的控制系統通過Profinet協議收集和監控渦輪機的狀態信息&#xff0c;同時發送控制命令。 風力…

因特網和萬維網

本文來源 &#xff1a;騰訊元寶 因特網&#xff08;Internet&#xff09;和萬維網&#xff08;World Wide Web&#xff0c;簡稱WWW&#xff09;是緊密相關但完全不同的兩個概念&#xff0c;它們的核心區別如下&#xff1a; 本質不同?? ??因特網&#xff08;Internet&#…

Visual Studio 技能:調整軟件界面布局

專欄導航 本節文章分別屬于《Win32 學習筆記》和《MFC 學習筆記》兩個專欄&#xff0c;故劃分為兩個專欄導航。讀者可以自行選擇前往哪個專欄。 &#xff08;一&#xff09;WIn32 專欄導航 上一篇&#xff1a;Windows編程&#xff1a;在VS2019里面&#xff0c;調整代碼字體大…

LeetCode 熱題 100_最小路徑和(92_64_中等_C++)(多維動態規劃)

LeetCode 熱題 100_最小路徑和&#xff08;92_64&#xff09; 題目描述&#xff1a;輸入輸出樣例&#xff1a;題解&#xff1a;解題思路&#xff1a;思路一&#xff08;多維動態規劃&#xff09;&#xff1a; 代碼實現代碼實現&#xff08;思路一&#xff08;多維動態規劃&…

Sql刷題日志(day6)

一、筆試 1、insert ignore&#xff1a;在插入數據時忽略主鍵沖突或其他唯一性約束沖突。 如果插入的記錄會導致主鍵沖突&#xff08;如 actor_id 已存在&#xff09;&#xff0c;該語句不會報錯&#xff0c;而是直接忽略插入操作 語法&#xff1a; INSERT IGNORE INTO tab…

Java多線程入門案例詳解:繼承Thread類實現線程

本文通過一個簡單案例&#xff0c;講解如何通過繼承 Thread 類來實現多線程程序&#xff0c;并詳細分析了代碼結構與運行機制。 一、前言 在 Java 中&#xff0c;實現多線程主要有兩種方式&#xff1a; 繼承 Thread 類 實現 Runnable 接口 本文以繼承 Thread 類為例&#x…

Netty在線客服系統落地方案

本文不講然后代碼方面的東西&#xff0c;只聊方案&#xff01;&#xff01; 這方案基于 Spring Boot 2.6、Netty、MyBatis Plus、Redis 構建的一套支持 單體應用 的在線客服系統。 系統支持客戶自由與后臺客服實時聊天、客服未在線釘釘提醒通知客服、消息已讀未讀標記、消息已…

SDK游戲盾、高防IP、高防CDN三者的區別與選型指南

在網絡安全防護領域&#xff0c;SDK游戲盾、高防IP和高防CDN是常見的解決方案&#xff0c;但各自的功能定位、技術實現和適用場景差異顯著。本文將通過對比核心差異&#xff0c;幫助您快速理解三者特點并選擇適合的防護方案。 一、核心功能定位 SDK游戲盾 功能核心&#xff1a…

GRPO有什么缺點,如何改進?

一、GRPO的核心原理與設計目標 Group Relative Policy Optimization(GRPO)是DeepSeek團隊提出的一種強化學習算法,旨在解決傳統PPO(Proximal Policy Optimization)在大語言模型(LLM)訓練中的資源消耗問題。其核心創新在于 通過組內相對獎勵替代價值函數(Critic Model)…

登高架設作業指的是什么?有什么安全操作規程?

登高架設作業是指在高處從事腳手架、跨越架架設或拆除的作業。具體包括以下方面&#xff1a; 腳手架作業 搭建各類腳手架&#xff0c;如落地式腳手架、懸挑式腳手架、附著式升降腳手架等&#xff0c;為建筑施工、設備安裝、高處維修等作業提供安全穩定的工作平臺。對腳手架進行…

前端實現商品放大鏡效果(Vue3完整實現)

前端實現商品放大鏡效果&#xff08;Vue3完整實現&#xff09; 前言 在電商類項目中&#xff0c;商品圖片的細節展示至關重要。放大鏡效果能顯著提升用戶體驗&#xff0c;允許用戶在不跳轉頁面的情況下查看高清細節。本文將基于Vue3實現一個高性能的放大鏡組件&#xff0c;完整…

【C++11特性】Lambda表達式(匿名函數)

一、函數對象 在C中&#xff0c;我們把所有能當作函數使用的對象當作函數對象。 一般來說&#xff0c;如果我們列出一個對象&#xff0c;而它的后面又跟有由花括號包裹的參數列表&#xff0c;就像fun(arg1, arg2, …)&#xff0c;這個對象就被稱為函數對象。函數對象大致可分為…

大模型在肝硬化腹水風險預測及臨床方案制定中的應用研究

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的與創新點 1.3 研究方法與數據來源 二、肝硬化及大模型相關理論基礎 2.1 肝硬化概述 2.2 大模型技術原理 2.3 大模型在醫療領域的應用現狀 三、大模型預測肝硬化腹水術前風險 3.1 術前風險因素分析 3.2 大模型預測術前…