C++構造函數與初始化全面指南:從基礎到高級實踐

C++構造函數與初始化全面指南:從基礎到高級實踐

1. 構造函數基礎概念

構造函數是C++中一種特殊的成員函數,它在創建類對象時自動調用,用于初始化對象的數據成員。構造函數的核心特點包括:

  • 與類同名
  • 無返回類型(連void都沒有)
  • 可以重載(一個類可以有多個構造函數)
  • 通常聲明為public(除非有特殊需求)

1.1 默認構造函數

默認構造函數是不需要任何參數的構造函數。如果用戶沒有定義任何構造函數,編譯器會自動生成一個默認構造函數。

class MyClass {
public:MyClass() { // 默認構造函數std::cout << "默認構造函數被調用" << std::endl;}
};// 使用
MyClass obj; // 調用默認構造函數

2. 構造函數初始化方式

2.1 賦值初始化(傳統方式)

class Point {int x;int y;
public:Point(int a, int b) {x = a; // 賦值初始化y = b; // 賦值初始化}
};

2.2 初始化列表(推薦方式)

C++更推薦使用成員初始化列表,它在構造函數體執行前完成初始化,效率更高。對于const成員和引用成員,初始化列表是必須使用的唯一方式。

為什么const成員和引用成員必須使用初始化列表?

  1. const成員的不可變性

    • const成員一旦被初始化后,其值不可再修改
    • 如果允許在構造函數體內賦值,這實際上是對const成員的二次賦值(編譯器會先默認初始化,再嘗試賦值),違反了const語義
  2. 引用成員的本質

    • 引用是別名,必須在創建時綁定到一個已存在的對象
    • 引用一旦綁定后,無法再指向其他對象
    • 在構造函數體內"初始化"引用會導致引用在聲明時未綁定(非法)
class ConstRefDemo {const int constValue;  // const成員int& refValue;         // 引用成員int normalValue;       // 普通成員
public:// const和引用成員必須使用初始化列表ConstRefDemo(int cv, int& rv) : constValue(cv), refValue(rv) {normalValue = 0;  // 普通成員可以在構造函數體內賦值}// 錯誤示例:/*ConstRefDemo(int cv, int& rv) {constValue = cv;  // 錯誤!const成員不能在構造函數體內賦值refValue = rv;    // 錯誤!引用必須在定義時綁定normalValue = 0;}*/
};

初始化列表的優勢

  1. 對于const成員和引用成員,必須使用初始化列表
  2. 對于類類型成員,避免先默認構造再賦值
  3. 初始化順序更明確(按成員聲明順序而非列表順序)
  4. 效率更高,直接初始化而非先默認構造再賦值

3. 特殊構造函數

3.1 拷貝構造函數

拷貝構造函數用于用一個已存在的對象初始化新對象。

class MyString {char* data;
public:MyString(const MyString& other) { // 拷貝構造函數data = new char[strlen(other.data) + 1];strcpy(data, other.data);}
};

3.2 移動構造函數(C++11)

移動構造函數用于"竊取"臨時對象的資源,避免不必要的拷貝。

class MyString {char* data;
public:MyString(MyString&& other) noexcept : data(other.data) { // 移動構造函數other.data = nullptr; // 使原對象處于有效但未定義狀態}
};

4. 委托構造函數(C++11)

一個構造函數可以調用同類的另一個構造函數,避免代碼重復。

class Rectangle {int width, height;
public:Rectangle() : Rectangle(1, 1) {} // 委托給下面的構造函數Rectangle(int w, int h) : width(w), height(h) {}
};

5. 初始化順序問題

成員的初始化順序取決于它們在類中的聲明順序,而非初始化列表中的順序。這對const成員和引用成員尤其重要,因為它們必須正確初始化。

class Example {int a;const int b;  // const成員int& c;       // 引用成員
public:Example(int val) : c(a), b(val), a(10) {} // 實際初始化順序:a(10) → b(val) → c(a)// 注意:雖然初始化列表中c寫在前面,但實際按聲明順序初始化
};

6. 特殊成員的初始化

6.1 const成員初始化

const成員必須在初始化列表中初始化。

class ConstDemo {const int value;
public:ConstDemo(int v) : value(v) {} // 必須這樣初始化// 錯誤示例:/*ConstDemo(int v) {value = v; // 錯誤!const成員不能在構造函數體內賦值}*/
};

6.2 引用成員初始化

引用成員也必須在初始化列表中初始化。

class RefDemo {int& ref;
public:RefDemo(int& r) : ref(r) {} // 必須這樣初始化// 錯誤示例:/*RefDemo(int& r) {ref = r; // 錯誤!引用必須在定義時綁定}*/
};

7. 默認構造函數與=default

C++11允許顯式要求編譯器生成默認實現:

class DefaultDemo {const int value = 42; // C++11允許類內初始化const成員std::string& ref;     // 引用仍然必須在構造函數中初始化
public:DefaultDemo(std::string& s) : ref(s) {} // 引用必須在這里初始化DefaultDemo() = delete; // 禁止默認構造(因為引用必須初始化)
};

8. 構造函數實戰建議

  1. 優先使用初始化列表:特別是對于類類型成員、const成員和引用成員
  2. 注意初始化順序:按照成員聲明順序編寫初始化列表
  3. const和引用成員的特殊處理:它們必須在初始化列表中初始化
  4. 合理使用explicit:防止單參數構造函數的隱式轉換
  5. 考慮=default和=delete:明確表達設計意圖
  6. 移動語義:對于資源管理類,實現移動構造和移動賦值

9. 完整示例代碼(包含const和引用成員)

#include <iostream>
#include <string>class Student {
private:const std::string name;  // const成員int& ageRef;             // 引用成員const int id;            // const成員double scores[3];public:// 委托構造函數Student(std::string n, int& age, int i) : name(std::move(n)), ageRef(age), id(i) { // const和引用成員必須在此初始化for (double & score : scores) {score = 0.0;}}// 不能有默認構造函數,因為引用成員必須初始化// Student() = delete; void printInfo() const {std::cout << "Name: " << name << "\nID: " << id << "\nAge: " << ageRef << "\nScores: ";for (double score : scores) {std::cout << score << " ";}std::cout << std::endl;}// 不能修改const成員// void setName(const std::string& newName) { name = newName; } // 錯誤!// 可以通過引用成員修改原變量void incrementAge() { ageRef++; }
};int main() {int age = 20;Student s1("Alice", age, 1001);s1.printInfo();age = 21;  // 修改age會影響s1中的ageRefs1.incrementAge(); // 通過引用成員修改原變量s1.printInfo();return 0;
}

10. 關鍵總結

  1. const成員和引用成員必須在初始化列表中初始化,這是語言強制要求的
  2. 初始化列表提供了真正的初始化能力,而構造函數體內只是賦值
  3. 這種設計保證了對象構造時的確定性和安全性
  4. 現代C++實踐中,應該優先使用初始化列表,不僅是為了滿足語法要求,更是為了編寫更高效、更安全的代碼

理解并正確使用構造函數,特別是對const成員和引用成員的正確初始化,是C++面向對象編程的重要基礎。這些規則反映了C++對確定性和效率的核心追求。

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

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

相關文章

大模型長對話中上下文無法承載全部歷史,如何壓縮或提取重點

在人工智能技術迅猛發展的今天,大模型已經滲透到我們生活的方方面面,尤其是自然語言處理領域,簡直是掀起了一場革命。從智能客服到個人助手,從在線教育到心理咨詢,大模型驅動的對話系統正在以一種前所未有的方式改變我們與機器的互動模式。特別是那些能夠進行多輪對話、甚…

ubuntu20.04安裝教程(圖文詳解)

Ubuntu 24.04 LTS&#xff0c;代號 Noble Numbat&#xff0c;于 2024 年 4 月 25 日發布&#xff0c;現在可以從 Ubuntu 官方網站及其鏡像下載。此版本將在 2029 年 4 月之前接收為期五年的官方安全和維護更新。 關于 Ubuntu 24.04 LTS 的一些關鍵點&#xff1a; 發布日期&am…

數據結構之隊列:原理與應用

一、基本原理 隊列是一種特殊的線性表隊列是一個有序表(可以用數組或鏈表實現)遵循“先來先服務”的原則&#xff0c;它只允許在表的前端&#xff08;隊頭&#xff09;進行刪除操作&#xff0c;在表的后端&#xff08;隊尾&#xff09;進行插入操作 (一) 核心操作 入隊&…

Ubuntu 安裝 Miniconda 及配置國內鏡像源完整指南

目錄 Miniconda 安裝Conda 鏡像源配置Pip 鏡像源配置驗證配置基本使用常見問題 1. Miniconda 安裝 1.1 下載安裝腳本 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh1.2 執行安裝 bash Miniconda3-latest-Linux-x86_64.sh按回車查看許可協議…

PYTHON通過VOSK實現離線聽寫支持WINDOWSLinux_X86架構

在當今人工智能快速發展的時代&#xff0c;語音識別技術已經成為人機交互的重要方式之一。本文將介紹如何使用Python結合Vosk和PyAudio庫實現一個離線語音識別系統&#xff0c;無需依賴網絡連接即可完成語音轉文字的功能。 技術棧概述 1. Vosk語音識別引擎 Vosk是一個開源的…

【Java進階】圖像處理:從基礎概念掌握實際操作

一、核心概念&#xff1a;BufferedImage - 圖像的畫布與數據載體 在Java圖像處理的世界里&#xff0c;BufferedImage是當之無愧的核心。你可以將它想象成一塊內存中的畫布&#xff0c;所有的像素數據、顏色模型以及圖像的寬度、高度等信息都存儲在其中。 BufferedImage繼承自…

數據治理系統是什么?數據治理工具有什么用?

目錄 一、數據治理系統是什么&#xff1f; 二、數據治理系統的重要性 1. 保障數據質量 2. 確保數據安全 3. 促進數據共享與協作 三、常見的數據治理工具及其特點 1. 數據質量管理工具 2. 數據集成工具 3. 元數據管理工具 四、數據治理工具有哪些作用&#xff1f; 1.…

消息隊列-kafka為例

目錄 消息隊列應用場景和基礎知識MQ常見的應用場景MQ消息隊列的兩種消息模式如何保證消息隊列的高可用&#xff1f;如何保證消息不丟失&#xff1f;如何保證消息不被重復消費&#xff1f;如何保證消息消費的冪等性&#xff1f;重復消費的原因解決方案 如何保證消息被消費的順序…

C++17常量

nullptr nullptr出現的目的是為了替代NULL。在某種意義上來說&#xff0c;傳統會把NULL,0視為同一種東 西&#xff0c;這取決于編譯器如何定義NULL&#xff0c;有些編譯器會將定義為((void*)0)&#xff0c;有些則會直接將其定義 為0。 C不允許直接將void*隱式轉換到其他類型。…

計算機網絡學習(九)——CDN

一、CDN CDN&#xff08;Content Delivery Network&#xff0c;內容分發網絡&#xff09;是一種通過分布式節點將內容更高效地傳遞給用戶的技術架構&#xff0c;廣泛應用于加速網站、視頻、下載、直播等業務。 CDN 是把內容放到離用戶最近的“高速公路入口”&#xff0c;提升訪…

Elasticsearch的寫入流程介紹

Elasticsearch 的寫入流程是一個涉及 分布式協調、分片路由、數據同步和副本更新 的復雜過程,其設計目標是確保數據一致性、可靠性和高性能。以下是寫入流程的詳細解析: 一、寫入流程總覽 二、詳細步驟解析 1. 客戶端請求路由 請求入口:客戶端(如 Java 客戶端、REST API)…

vue為什么點擊兩遍才把參數傳遞過去

先說一下場景&#xff0c;就是我把云服務器這個下拉選擇框分別初始化之后&#xff0c;然后點擊新建權限然后就打開了右側的抽屜式的對話框&#xff0c;頁面上那個文字信息是傳遞過來了。那個是正確的&#xff0c;但是我請求接口的時候&#xff0c;發現請求的接口的參數總是要慢…

java代碼性能優化

刷題過程中遇到的一些時間復雜度相同&#xff0c;但是常數因子的差距導致的性能差距&#xff0c;遇到持續更新 枚舉 VS contains 例如&#xff1a;判斷一個字符是不是元音 法一&#xff1a; if(ch a || ch e || ch i || ch o || ch u) 法二&#xff1a; Set<Charact…

OpenGL Chan視頻學習-9 Index Buffers inOpenGL

bilibili視頻鏈接&#xff1a; 【最好的OpenGL教程之一】https://www.bilibili.com/video/BV1MJ411u7Bc?p5&vd_source44b77bde056381262ee55e448b9b1973 函數網站&#xff1a; docs.gl 說明&#xff1a; 1.之后就不再單獨整理網站具體函數了&#xff0c;網站直接翻譯會…

基于微服務架構的社交學習平臺WEB系統的設計與實現

設計&#xff08;論文&#xff09;題目 基于微服務架構的社交學習平臺WEB系統的設計與實現 摘 要 社交學習平臺 web 系統要為學習者打造一個開放、互動且社交性強的在線教育環境&#xff0c;打算采用微服務架構來設計并實現一個社交學習平臺 web 系統&#xff0c;以此適應學…

生成式人工智能:重構軟件開發的范式革命與未來生態

引言 生成式人工智能&#xff08;GenAI&#xff09;正以顛覆性力量重塑軟件開發的底層邏輯。從代碼生成到業務邏輯設計&#xff0c;從數據分析到用戶交互&#xff0c;GenAI通過其強大的推理能力與場景適應性&#xff0c;將傳統開發流程的“復雜工程”轉化為“敏捷實驗”&#…

C++17原生測試編程實踐:現代特性與分支覆蓋指南

C17原生測試編程實踐&#xff1a;現代特性與分支覆蓋指南 概述 本文將深入探討如何利用C17新特性進行原生測試代碼編寫&#xff0c;實現完全分支覆蓋。我們將不依賴任何外部測試框架&#xff0c;而是使用C17標準庫構建完整的測試解決方案。 一、C17測試核心工具集 1. 斷言工…

RK3568項目(四)--uboot啟動流程之啟動模式選擇

目錄 一、引言 二、芯片初始化 ------>2.1、io_domain ------>2.2、調頻調壓 ------>2.3、控制臺初始化 三、平臺初始化 ------>3.1、設置mac地址 ------------>3.1.1、vendor分區 ------>3.2、設置serialno ------>3.3、設置下載模式 -------…

Kotlin JVM 注解詳解

前言 Kotlin 作為一門現代 JVM 語言&#xff0c;提供了出色的 Java 互操作性。為了更好地支持與 Java 代碼的交互&#xff0c;Kotlin 提供了一系列 JVM 相關注解。這些注解不僅能幫助我們控制 Kotlin 代碼編譯成 Java 字節碼的行為&#xff0c;還能讓我們的 Kotlin 代碼更好地…

Starrocks 物化視圖的實現以及在刷新期間能否讀數據

背景 本司在用Starrocks做一些業務上的分析的時候&#xff0c;用到了物化視圖&#xff0c;并且在高QPS的情況下&#xff0c;RT也沒有很大的波動&#xff0c;所以在此研究一下Starrock的實現&#xff0c;以及在刷新的時候是不是原子性的 本文基于Starrocks 3.3.5 結論 Starro…