首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

深入理解 C++ 面向對象原理(一):繼承中的同名成員遮蔽與構造順序

     編輯:關於C語言

代碼實驗

#include <iostream>
using namespace std;

class A {
public:
    int _a;
    A() { _a = 1; printf("A construct\n"); }
    void print() { printf("%d,", _a); }
};

class B : public A {
public:
    int _a;  // 同名成員遮蔽基類
    B() { _a = 2; printf("B construct\n"); }
};

int main() {
    B b;
    b.print();        // 調用基類 A::print()
    printf("%d", b._a); // 訪問派生類 B::_a
    return 0;
}

輸出結果

A construct
B construct
1,2

核心機制深度解析

1. 構造函數調用順序

  • 基類構造函數 先於 派生類構造函數執行
  • 輸出先印 "A construct" 再印 "B construct" 證明此順序
  • 基類構造完成後,派生類構造才開始,保證基類子對象已初始化

2. 同名成員遮蔽而非覆蓋

  • B::_a 遮蔽 A::_a,兩者獨立佔用內存
  • 對象佈局:[A::_a][B::_a](單繼承無虛函數時)
  • b.print() 調用 A::print(),內部訪問 this->_a 解析為 A::_a → 輸出 1
  • b._a 直接訪問派生類作用域成員 → 輸出 2

3. 內存佈局圖解

B 對象內存:
+--------+--------+
| A::_a  | B::_a  |  ← 兩個獨立 int
+--------+--------+
  offset0 offset4  (32位系統)

A::print() 編譯為:
    mov eax, [this]      ; this 指向 B 對象起始
    mov eax, [eax]       ; 取 offset 0 → A::_a
    push eax
    call printf

b._a 編譯為:
    mov eax, [this+4]    ; 取 offset 4 → B::_a
    push eax
    call printf

4. 訪問被遮蔽基類成員的方法

b.A::_a = 10;        // 顯式作用域限定
b.A::print();        // 顯式調用基類版本
static_cast(b)._a = 20; // 強轉基類引用

最佳實踐建議

  1. 避免同名成員:命名規範區分(如 m_a vs a_)
  2. 若必須同名:文檔說明遮蔽語義,提供基類訪問接口
  3. 構造函數:基類構造不可拋出異常(或確保資源安全),派生類構造中基類子對象已完全構造
  4. 析構函數:基類析構應聲明 virtual,否則刪除基類指針導致派生類析構不被調用

本文整理自「天才鳥蛋」博客,原文地址:https://curely.blog.51cto.com/1627940/1297701

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved