C++----面向对象编程之封装【通过访问控制符public、private、protected实现】

目录

C++ 中的封装(Encapsulation)

1. 为什么需要封装?/封装的意义

2. C++ 封装的实现【通过访问控制修饰符实现】

2.1 访问控制修饰符在代码中的具体形式

2.2 protected 访问控制修饰符的具体使用

3. 封装示例

(1)错误示例:没有封装,数据不安全

(2)正确示例:使用封装保护数据

4. 友元(friend)与封装

示例:使用 friend 访问 private 成员

5. 总结


C++ 中的封装(Encapsulation)

封装(Encapsulation)是面向对象编程(OOP)的核心特性之一,它的主要目的是将数据和操作数据的方法绑定在一起,并限制对数据的直接访问,从而提高代码的安全性和可维护性。

封装的核心思想就是 “把内部实现藏起来,对外提供有限的接口”,就像 ATM 机一样,你只能操作它允许的按钮,而不能直接干预它的运作

借助一个生活中的例子来理解封装: ATM 取款机 🏦💳

现实场景:在 ATM 机上,用户 只需要输入密码、选择金额,然后取钱,但 ATM 机的内部实现是对用户隐藏的。

  • 无法直接操作 ATM 机内部的电路,也看不到 ATM 如何与银行系统交互
  • 只能通过 ATM 提供的按钮和屏幕进行操作

这就像一个 封装良好的 C++ 类,它的内部运作是不可见的(私有的),但提供了一些公共接口(Public 方法)供用户使用。


1. 为什么需要封装?/封装的意义

  1. 数据安全性:防止对象的数据被外部代码随意修改,避免错误和不一致的状态。
  2. 隐藏实现细节外部代码不需要关心类的具体实现,只需使用对外提供的接口即可。封装允许我们隐藏类的内部实现,对外部代码只暴露必要的接口,使得程序更加模块化。这样,类的实现可以随时更改,而不会影响外部代码。
  3. 提高代码的可维护性:封装可以减少代码之间的耦合,使得修改代码时影响更小。封装使代码模块化,每个类负责自己的功能,避免了全局变量的混乱,提高了代码的可维护性。
  4. 控制访问权限:通过 privateprotectedpublic 关键字控制数据的访问级别。

2. C++ 封装的实现【通过访问控制修饰符实现】

C++ 通过 访问控制修饰符privateprotectedpublic)来实现封装,使类的成员变量(数据)和成员函数(方法)具有不同的访问权限。通常,我们会:

  1. 将数据成员设为 private,防止外部直接访问
  2. 提供 public 方法来访问和修改数据
  3. 隐藏实现细节,只暴露必要的接口

封装允许我们通过 publicprivateprotected 关键字精确控制类成员的访问权限,避免外部代码误用类的内部数据。

访问控制类内部访问类外部访问子类访问
private✅ 可以❌ 不可以❌ 不可以
protected✅ 可以❌ 不可以✅ 可以
public✅ 可以✅ 可以✅ 可以

2.1 访问控制修饰符在代码中的具体形式

#include <iostream>
using namespace std;

class Example {
private:
    int privateVar;  // 私有成员,外部无法直接访问

protected:
    int protectedVar;  // 保护成员,子类可以访问

public:
    int publicVar;  // 公有成员,外部可以访问

    void setPrivateVar(int value) {
        privateVar = value;
    }

    int getPrivateVar() {
        return privateVar;
    }
};

int main() {
    Example obj;
    obj.publicVar = 10;  // ✅ 可以直接访问
    obj.setPrivateVar(5);  // ✅ 通过 public 方法访问 private 数据
    cout << "Private Variable: " << obj.getPrivateVar() << endl;
    
    // ❌ obj.privateVar = 20; // 编译错误,无法直接访问 private 变量
    // ❌ obj.protectedVar = 15; // 编译错误,无法直接访问 protected 变量
    return 0;
}

说明

  • privateVar 只能通过 setPrivateVar()getPrivateVar() 访问,实现封装
  • publicVar 可以被外部直接访问。
  • protectedVar 不能在 main() 中访问,但可以在子类中访问(见后面示例)。

2.2 protected 访问控制修饰符的具体使用

#include<iostream>

// protected 访问控制修饰符的使用
class BaseClass{
    protected:
    int base_protected_var=10; // 受保护成员,类内部可以访问,类外部不可以访问,子类可以访问
    void display(){
        std::cout<<"这是基类的受保护方法"<<std::endl;
    }
    public:
    int access_protected_InClass(){
        return base_protected_var; // 受保护成员,类内部可以直接访问
    }
    int access_protected_OutClass(){
        return base_protected_var; // 受保护成员,类外部不能直接访问,但可借助类内公有成员函数间接访问
    }
};

class DerivedClass : BaseClass {  // 子类(派生类)
    public:
    void show() {
        std::cout << "子类访问父类的 protected 变量:" << base_protected_var << std::endl;
    }
};

int main(){
    BaseClass b1; // 基类实例化
    // b1.base_var; // ❌ 受保护成员不能够在类外部被直接访问
    // b1.display(); // ❌ 受保护成员不能够在类外部被直接访问
    int result = b1.access_protected_OutClass(); // 受保护成员借助类内部公有成员函数在类外部简介访问
    std::cout<<result<<std::endl; // 输出:10

    DerivedClass child; // 子类实例化
    child.show(); // 输出:子类访问父类的 protected 变量:10

    return 0;
}

protected 变量只能在 父类和子类内部 访问,不能被外部代码直接访问

protected 主要用于 继承,让子类可以访问但不暴露给外部代码。
private 变量 连子类都无法访问,更安全,但如果需要子类访问,就可以使用 protected
✅ 在 类外部,无论是 private 还是 protected,都不能直接访问。


3. 封装示例

(1)错误示例:没有封装,数据不安全

#include <iostream>
using namespace std;

class Student {
public:
    string name;
    int age;
};

int main() {
    Student s;
    s.name = "张三";  // 直接访问并修改
    s.age = 20;  // 直接访问并修改
    cout << "姓名: " << s.name << ", 年龄: " << s.age << endl;
    return 0;
}

问题:

  • nameage 直接暴露在外部,任何人都可以修改它们,可能导致数据不安全或不一致。

(2)正确示例:使用封装保护数据

#include <iostream>
using namespace std;

class Student {
private:
    string name;  // 私有数据成员
    int age;      // 私有数据成员

public:
    // 设置姓名
    void setName(string n) {
        name = n;
    }

    // 获取姓名
    string getName() {
        return name;
    }

    // 设置年龄(增加数据有效性检查)
    void setAge(int a) {
        if (a > 0 && a < 120) {  // 限制年龄范围
            age = a;
        } else {
            cout << "无效的年龄" << endl;
        }
    }

    // 获取年龄
    int getAge() {
        return age;
    }
};

int main() {
    Student s;
    s.setName("张三");
    s.setAge(20);
    cout << "姓名: " << s.getName() << ", 年龄: " << s.getAge() << endl;
    return 0;
}

改进:

  • 数据成员 nameage 设置为 private,外部不能直接访问,避免了随意修改。
  • 提供 setget 方法,并在 setAge 方法中增加年龄范围检查,保证数据有效性。


4. 友元(friend)与封装

在某些特殊情况下,我们可能希望外部函数或类可以访问 privateprotected 成员。C++ 提供了 friend 关键字 允许特定函数或类访问私有成员。

示例:使用 friend 访问 private 成员

#include <iostream>
using namespace std;

class Student {
private:
    string name;
    int age;

public:
    Student(string n, int a) : name(n), age(a) {}

    // 声明友元函数
    friend void showStudentInfo(Student s);
};

// 友元函数可以访问 private 成员
void showStudentInfo(Student s) {
    cout << "姓名: " << s.name << ", 年龄: " << s.age << endl;
}

int main() {
    Student s("张三", 20);
    showStudentInfo(s);  // 友元函数可以访问 private 成员
    return 0;
}

友元的作用:

  • 允许某个特定函数或类访问私有成员,但不影响封装的其他部分。
  • 常用于运算符重载、调试等场景

5. 总结

  1. 封装提高数据安全性,防止数据被外部随意修改。
  2. 隐藏实现细节,对外提供稳定的接口,降低代码耦合。
  3. 提高代码可维护性,修改代码时不会影响其他部分。
  4. 便于代码复用,封装良好的类可以在多个项目中使用。
  5. 控制访问权限,防止错误使用数据,提高代码的可靠性。
  6. 友元(friend)允许外部函数或类访问 private 成员,但要谨慎使用,否则可能破坏封装性。

封装是 C++ 面向对象编程 的重要特性,它帮助我们保护数据、减少耦合、提高代码可维护性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值