C++ private、protected、public從入門到精通實例解析
八股文又來了, 看吧 看一遍你就會了 時間長忘了?那就再看一遍孩子
一、核心概述
private、protected、public 是 C++ 封裝特性的核心載體,其本質(zhì)是在編譯期限制類成員(變量/函數(shù))在不同作用域下的可見性和訪問權限——簡單來說,就是規(guī)定“哪些地方能使用類的某個成員,哪些地方不能”。掌握這三個關鍵字的規(guī)則,是寫出符合工程化規(guī)范、高內(nèi)聚低耦合 C++ 代碼的基礎。
二、基礎定義 & 核心訪問規(guī)則
2.1 核心規(guī)則表(必記)
| 關鍵字 | 官方定義 | 大白話解釋 | 類內(nèi)訪問 | 類外訪問(通過對象) | 派生類(子類)訪問 |
|---|---|---|---|---|---|
public | 公有的,對外暴露的接口,無訪問限制 | 誰都能訪問:類自己能用、外部代碼能用、子類也能用 | ? 允許 | ? 允許 | ? 允許 |
private | 私有的,僅類內(nèi)部可見,封裝的核心體現(xiàn) | 只有類自己能用:外部代碼不能用、子類也不能用(友元除外) | ? 允許 | ? 禁止 | ? 禁止 |
protected | 受保護的,介于public和private之間,為派生類預留的訪問權限 | 類自己能用、子類能用,但外部代碼不能用 | ? 允許 | ? 禁止 | ? 允許 |
2.2 默認訪問控制(易錯點)
- 用
class定義類時,默認訪問控制是private(未寫關鍵字的成員均為 private); - 用
struct定義類時,默認訪問控制是public(C++ 對 C 的兼容設計,struct 僅默認權限與 class 不同)。
2.3 基礎驗證代碼
#include <iostream>
using namespace std;
class MyClass {
// 未寫關鍵字,class默認private
int default_val = 0;
public:
int pub_val = 10; // 公有成員
void pub_func() {
// 類內(nèi):能訪問所有成員(public/private/protected)
cout << "類內(nèi)訪問 private_val: " << pri_val << endl;
cout << "類內(nèi)訪問 protected_val: " << pro_val << endl;
cout << "類內(nèi)訪問 default_val: " << default_val << endl;
}
private:
int pri_val = 20; // 私有成員
void pri_func() { cout << "私有函數(shù)" << endl; }
protected:
int pro_val = 30; // 受保護成員
void pro_func() { cout << "受保護函數(shù)" << endl; }
};
// 派生類(子類)
class Derived : public MyClass {
public:
void derived_func() {
// 派生類:能訪問基類的public/protected,不能訪問private
cout << "派生類訪問 pub_val: " << pub_val << endl; // ? 允許
cout << "派生類訪問 pro_val: " << pro_val << endl; // ? 允許
// cout << "派生類訪問 pri_val: " << pri_val << endl; // ? 禁止:private不可訪問
pro_func(); // ? 允許調(diào)用protected函數(shù)
// pri_func(); // ? 禁止調(diào)用private函數(shù)
}
};
int main() {
MyClass obj;
// 類外:僅能訪問public成員
cout << "類外訪問 pub_val: " << obj.pub_val << endl; // ? 允許
// cout << "類外訪問 pro_val: " << obj.pro_val << endl; // ? 禁止:protected不可訪問
// cout << "類外訪問 pri_val: " << obj.pri_val << endl; // ? 禁止:private不可訪問
obj.pub_func(); // ? 允許調(diào)用public函數(shù)
// obj.pro_func(); // ? 禁止調(diào)用protected函數(shù)
// obj.pri_func(); // ? 禁止調(diào)用private函數(shù)
Derived d_obj;
d_obj.derived_func(); // ? 派生類內(nèi)部邏輯正常執(zhí)行
return 0;
}2.4 補充:同一類不同對象的訪問規(guī)則
訪問控制是“類級別”的限制,而非“對象級別”——同一個類的不同對象,可互相訪問對方的 private/protected 成員(編譯期認為屬于“同一類的內(nèi)部”):
#include <iostream>
using namespace std;
class MyClass {
private:
int m_val = 10;
public:
// 訪問同類型另一個對象的private成員
void accessAnotherObj(MyClass& other) {
cout << "訪問另一個對象的private成員:" << other.m_val << endl; // ? 允許
other.m_val = 20; // ? 可修改
}
int getVal() const { return m_val; }
};
int main() {
MyClass a, b;
a.accessAnotherObj(b); // 輸出:訪問另一個對象的private成員:10
cout << "修改后b的val:" << b.getVal() << endl; // 輸出:20
return 0;
}三、繼承中的訪問控制(面試必考)
派生類繼承基類時,繼承方式(public/protected/private) 會修改“基類成員在派生類中的訪問權限”,核心鐵律:
派生類對基類成員的訪問權限 = 「基類成員自身的訪問權限」和「繼承方式」中更嚴格的那個。
3.1 繼承權限影響表
| 基類成員訪問權限 | public繼承(最常用) | protected繼承 | private繼承 |
|---|---|---|---|
| public | public(不變) | protected | private |
| protected | protected(不變) | protected | private |
| private | 不可訪問(不變) | 不可訪問 | 不可訪問 |
3.2 單層繼承代碼示例
#include <iostream>
using namespace std;
// 基類
class Base {
public:
int pub_val = 1;
protected:
int pro_val = 2;
private:
int pri_val = 3;
};
// 1. public繼承(開發(fā)中99%的場景,推薦)
class DerivedPub : public Base {
public:
void show() {
cout << "pub_val: " << pub_val << endl; // ? public → public
cout << "pro_val: " << pro_val << endl; // ? protected → protected
// cout << pri_val << endl; // ? 始終不可訪問
}
};
// 2. protected繼承
class DerivedPro : protected Base {
public:
void show() {
cout << "pub_val: " << pub_val << endl; // ? public → protected
cout << "pro_val: " << pro_val << endl; // ? protected → protected
}
};
// 3. private繼承
class DerivedPri : private Base {
public:
void show() {
cout << "pub_val: " << pub_val << endl; // ? public → private
cout << "pro_val: " << pro_val << endl; // ? protected → private
}
};
int main() {
DerivedPub d_pub;
cout << d_pub.pub_val << endl; // ? public繼承后,基類public仍為public,類外可訪問
// cout << d_pub.pro_val << endl; // ? protected,類外不可訪問
DerivedPro d_pro;
// cout << d_pro.pub_val << endl; // ? public繼承后變?yōu)閜rotected,類外不可訪問
DerivedPri d_pri;
// cout << d_pri.pub_val << endl; // ? public繼承后變?yōu)閜rivate,類外不可訪問
return 0;
}3.3 多層繼承的權限鏈式影響
多層繼承中,基類成員權限會隨繼承鏈“層層收緊”(僅會更嚴格,不會放寬):
#include <iostream>
using namespace std;
// 頂層基類
class A {
public:
int pub_a = 1;
protected:
int pro_a = 2;
};
// 第二層:B public繼承A
class B : public A {
public:
void showB() {
cout << "B中訪問A的pub_a:" << pub_a << endl; // ? public→public
cout << "B中訪問A的pro_a:" << pro_a << endl; // ? protected→protected
}
};
// 第三層:C protected繼承B
class C : protected B {
public:
void showC() {
// A的pub_a:A→B(public)→C(protected)→ 最終C中是protected
cout << "C中訪問A的pub_a:" << pub_a << endl; // ? 允許(C內(nèi)可訪問protected)
// A的pro_a:A→B(protected)→C(protected)→ 最終C中是protected
cout << "C中訪問A的pro_a:" << pro_a << endl; // ? 允許
}
};
// 第四層:D private繼承C
class D : private C {
public:
void showD() {
// A的pub_a:經(jīng)過三層繼承后變?yōu)閜rivate
cout << "D中訪問A的pub_a:" << pub_a << endl; // ? D內(nèi)可訪問private
}
};
int main() {
C c;
// cout << c.pub_a << endl; // ? C中pub_a是protected,類外不可訪問
D d;
// cout << d.pub_a << endl; // ? D中pub_a是private,類外不可訪問
return 0;
}四、特殊場景的訪問控制細節(jié)
4.1 靜態(tài)成員的訪問控制
static 僅改變成員的“存儲方式”(屬于類而非對象),訪問控制規(guī)則與普通成員完全一致:
#include <iostream>
using namespace std;
class MyClass {
private:
static int s_pri_val; // 靜態(tài)私有成員
protected:
static int s_pro_val; // 靜態(tài)受保護成員
public:
static int s_pub_val; // 靜態(tài)公有成員
static void showStatic() {
// 類內(nèi)可訪問所有靜態(tài)成員
cout << "靜態(tài)私有:" << s_pri_val << endl;
cout << "靜態(tài)受保護:" << s_pro_val << endl;
}
};
// 靜態(tài)成員必須類外初始化
int MyClass::s_pri_val = 10;
int MyClass::s_pro_val = 20;
int MyClass::s_pub_val = 30;
// 派生類
class Derived : public MyClass {
public:
static void showDerivedStatic() {
// 派生類可訪問基類的static protected/public
cout << "派生類訪問靜態(tài)受保護:" << s_pro_val << endl; // ?
cout << "派生類訪問靜態(tài)公有:" << s_pub_val << endl; // ?
// cout << s_pri_val << endl; // ? 靜態(tài)私有仍不可訪問
}
};
int main() {
// 類外僅能訪問靜態(tài)公有成員
cout << MyClass::s_pub_val << endl; // ? 30
// cout << MyClass::s_pro_val << endl; // ? 靜態(tài)受保護,類外不可訪問
// cout << MyClass::s_pri_val << endl; // ? 靜態(tài)私有,類外不可訪問
Derived::showDerivedStatic(); // ? 派生類內(nèi)可訪問靜態(tài)受保護
return 0;
}4.2 protected 的深層細節(jié)(易踩坑)
protected 允許“子類訪問自己的基類成員”,但禁止子類訪問其他子類對象的基類 protected 成員:
#include <iostream>
using namespace std;
class Base {
protected:
int m_val = 10;
};
class DerivedA : public Base {
public:
// 錯誤場景:DerivedA試圖訪問DerivedB對象的protected成員
void accessDerivedB(DerivedB& b) {
// cout << b.m_val << endl; // ? 禁止:不能訪問其他子類對象的protected
}
// 正確場景:訪問自己的protected成員
void accessSelf() {
cout << m_val << endl; // ? 允許
}
};
class DerivedB : public Base {}; // 另一個子類
int main() {
DerivedA a;
DerivedB b;
a.accessSelf(); // ? 輸出10
// a.accessDerivedB(b); // ? 編譯報錯
return 0;
}五、友元:突破訪問控制的唯一機制
friend 是 C++ 中唯一能突破 private/protected 訪問限制的語法,核心是“顯式授權”——只有被類主動聲明為友元的函數(shù)/類,才能訪問其私有/受保護成員。友元按授權粒度從粗到細分為以下三種形式:
5.1 友元函數(shù)(單個函數(shù)授權)
友元函數(shù)是最基礎的友元形式,授權單個普通函數(shù)訪問類的私有/受保護成員,適用于“僅需一個外部函數(shù)訪問類內(nèi)部數(shù)據(jù)”的場景(如運算符重載、簡單的外部工具函數(shù))。
#include <iostream>
using namespace std;
class MyClass {
private:
int pri_val = 100;
// 聲明友元函數(shù):僅授權showPrivate函數(shù)訪問當前類的私有成員
friend void showPrivate(MyClass& obj);
};
// 友元函數(shù):可以直接訪問MyClass的private成員(無需通過public接口)
void showPrivate(MyClass& obj) {
cout << "訪問private成員: " << obj.pri_val << endl; // ? 允許
}
int main() {
MyClass obj;
showPrivate(obj); // 輸出:訪問private成員: 100
return 0;
}核心總結(jié):
- 授權粒度:僅單個函數(shù),影響范圍最小;
- 聲明位置:友元函數(shù)的聲明可寫在類的
public/private/protected任意區(qū)域,效果完全一致; - 核心優(yōu)勢:無需為單個函數(shù)開放過多權限,兼顧封裝性和實用性。
5.2 友元類(批量授權,整個類的所有成員函數(shù))
友元類是“批量授權”形式,授權另一個類的所有成員函數(shù)(無論 public/private/protected)訪問當前類的私有/受保護成員,適用于“兩個類高度耦合且需要深度協(xié)作”的場景(如容器類和其迭代器類)。
#include <iostream>
using namespace std;
class A {
// 聲明B為友元類:B的所有成員函數(shù)都能訪問A的私有/受保護成員
friend class B;
private:
int m_val = 100;
};
class B {
public:
// B的公有成員函數(shù):訪問A的私有成員
void accessA(A& a) {
cout << a.m_val << endl; // ? 允許
}
private:
// B的私有成員函數(shù):同樣能訪問A的私有成員(友元類的所有成員函數(shù)都有權限)
void privateAccessA(A& a) {
a.m_val = 200; // ? 允許修改
}
};
int main() {
A a;
B b;
b.accessA(a); // 輸出:100
return 0;
}核心總結(jié):
- 授權粒度:整個類的所有成員函數(shù),授權范圍最廣;
- 風險提示:友元類會大幅降低封裝性,僅在確有必要時使用(優(yōu)先考慮友元函數(shù)/友元成員函數(shù));
- 訪問規(guī)則:友元類的私有成員函數(shù)也能訪問授權類的私有成員,權限無差別。
5.3 友元成員函數(shù)(精細授權,僅單個成員函數(shù))
友元成員函數(shù)是“精準授權”形式,僅授權另一個類的某個特定成員函數(shù)訪問當前類的私有/受保護成員,是兼顧“協(xié)作需求”和“封裝性”的最優(yōu)友元形式。
#include <iostream>
using namespace std;
// 前置聲明:必須先聲明A,才能在B的成員函數(shù)參數(shù)中使用A的引用
class A;
class B {
public:
// 先聲明需要授權的成員函數(shù)(僅該函數(shù)需要訪問A的私有成員)
void onlyThisFuncCanAccessA(A& a);
};
class A {
// 僅授權B類的onlyThisFuncCanAccessA成員函數(shù)訪問當前類的私有成員
friend void B::onlyThisFuncCanAccessA(A& a);
private:
int m_val = 500;
};
// 實現(xiàn)授權的成員函數(shù):可直接訪問A的私有成員
void B::onlyThisFuncCanAccessA(A& a) {
cout << a.m_val << endl; // ? 允許
}
int main() {
A a;
B b;
b.onlyThisFuncCanAccessA(a); // 輸出:500
return 0;
}核心總結(jié):
- 授權粒度:僅單個成員函數(shù),是最精細的友元授權方式;
- 前置聲明:必須先聲明被授權的類(如A),再聲明其成員函數(shù),否則編譯器無法識別;
- 最佳實踐:工程開發(fā)中優(yōu)先使用友元成員函數(shù),最小化破壞封裝性。
5.4 友元的核心特性(所有友元形式通用)
友元關系的核心規(guī)則不受授權形式影響,需牢記以下三點:
- 單向性:若A聲明B為友元,僅B能訪問A的私有成員,A不能訪問B的私有成員(雙向授權需雙方互相聲明);
- 不傳遞性:若A授權B,B授權C,C不能訪問A的私有成員(友元關系無法鏈式傳遞);
- 不繼承性:若A授權B,C繼承B,C不能訪問A的私有成員(友元關系無法被子類繼承)。
六、實際開發(fā)中的應用場景 & 反模式
6.1 核心應用場景(最佳實踐)
| 關鍵字 | 核心應用場景 | 示例 |
|---|---|---|
public | 對外提供的「接口」(穩(wěn)定、不輕易修改) | 業(yè)務方法(如 calculate())、get/set函數(shù)(如 getName()/setName()) |
private | 類的「內(nèi)部實現(xiàn)細節(jié)」(隱藏,避免外部篡改) | 成員變量(如 m_name)、內(nèi)部輔助函數(shù)(如 checkValid()) |
protected | 基類中需要給「派生類復用」,但不希望外部訪問的成員 | 基類的通用屬性(如 m_id)、通用方法(如 initData()) |
6.2 常見反模式(嚴禁使用)
反模式1:成員變量設為 public
// 錯誤
class Student {
public:
int m_age; // 外部可直接賦值 m_age = -5,數(shù)據(jù)完全不安全
};
// 正確
class Student {
private:
int m_age;
public:
void setAge(int age) {
if (age >= 0 && age <= 120) m_age = age; // 帶校驗的寫接口
}
int getAge() const { return m_age; } // 只讀接口
};反模式2:為方便將 protected 改為 public
// 錯誤
class Base {
public:
int m_id; // 本應給子類復用的成員,被外部隨意修改
};
// 正確
class Base {
protected:
int m_id;
public:
int getId() const { return m_id; } // 僅暴露只讀接口
};反模式3:過度使用友元,破壞封裝
// 錯誤:授權多個類/函數(shù),封裝形同虛設
class A {
friend class B;
friend class C;
friend void func1();
friend void func2();
private:
int m_data;
};
// 正確:僅授權必要的函數(shù)/類
class A {
friend void onlyNecessaryFunc(A& a);
private:
int m_data;
};七、常見誤區(qū) & 避坑點
- 誤區(qū)1:
protected是“派生類的 private”→ 派生類之間不能訪問對方的protected成員; - 誤區(qū)2:繼承方式會修改基類自身的成員權限 → 僅修改“基類成員在派生類中的權限”,基類自身權限不變;
- 誤區(qū)3:
static會改變訪問控制 →static private仍僅類內(nèi)可訪問,static不影響權限; - 誤區(qū)4:訪問控制針對“對象”→ 同一類的不同對象可互相訪問 private 成員(類級別限制);
- 誤區(qū)5:
private絕對不可訪問 → 友元是合法途徑,指針強制轉(zhuǎn)換是不規(guī)范黑科技,嚴禁使用; - 誤區(qū)6:
struct沒有訪問控制 →struct只是默認權限為 public,同樣支持private/protected。
八、面試高頻考點 & 標準答案
8.1 基礎考點
- 問:
class和struct的訪問控制默認值有什么區(qū)別?
答:class默認訪問控制是private,struct默認是public(C++ 對 C 的兼容設計,struct 保留 C 的特性); - 問:
protected和private的核心區(qū)別是什么?
答:核心區(qū)別在派生類的訪問權限 ——protected允許派生類訪問,private不允許;類外兩者都不可訪問; - 問:public 繼承和 private 繼承的核心區(qū)別?
答:public 繼承保留基類public成員的公有屬性(類外可訪問),private 繼承會把基類所有可訪問成員變?yōu)?private(僅派生類內(nèi)可訪問);開發(fā)中優(yōu)先用 public 繼承; - 問:友元是否受訪問控制限制?
答:不受,友元函數(shù)/友元類可直接訪問類的private/protected成員,是訪問控制的合法例外。
8.2 進階考點(結(jié)合多態(tài))
問:為什么私有虛函數(shù)能被派生類重寫?
#include <iostream>
using namespace std;
class Base {
private:
virtual void func() { cout << "Base::func" << endl; }
public:
void callFunc() { func(); } // 公有接口調(diào)用私有虛函數(shù)
};
class Derived : public Base {
private:
void func() override { cout << "Derived::func" << endl; }
};
int main() {
Base* ptr = new Derived();
ptr->callFunc(); // 輸出:Derived::func
delete ptr;
return 0;
}答:訪問控制(private)和多態(tài)(虛函數(shù))是兩個獨立的機制:
- 訪問控制限制“誰能直接調(diào)用函數(shù)”;
- 虛函數(shù)重寫限制“函數(shù)是否能被覆蓋”;
派生類重寫私有虛函數(shù)后,基類的公有接口仍可調(diào)用重寫后的版本,這是 C++ 允許的合法設計。
九、核心總結(jié)
- 訪問控制的本質(zhì)是控制類成員的可見性,是編譯期語法檢查,核心為封裝性服務;
- 基礎規(guī)則:
public(類內(nèi)/外/派生類)、private(僅類內(nèi)+友元)、protected(類內(nèi)+派生類); - 繼承中:派生類對基類成員的權限 = 「基類成員權限」和「繼承方式」中更嚴格的那個,private 成員始終不可訪問;
- 特殊細節(jié):同一類的不同對象可互相訪問 private 成員,
protected僅允許子類訪問自身的基類成員; - 友元是唯一突破訪問控制的機制,遵循“單向、不傳遞、不繼承”,優(yōu)先用“友元成員函數(shù)”最小化授權;
- 開發(fā)最佳實踐:
public做接口、private藏實現(xiàn)、protected給子類復用,嚴禁 public 成員變量和過度友元。
到此這篇關于C++ private、protected、public從入門到精通實例解析的文章就介紹到這了,更多相關C++ private、protected、public內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
舉例剖析C++中引用的本質(zhì)及引用作函數(shù)參數(shù)的使用
這篇文章主要介紹了C++中引用的本質(zhì)及引用作函數(shù)參數(shù)的使用,講解了函數(shù)返回值是引用的情況等一些難點,需要的朋友可以參考下2016-03-03

