代码仓库shanchuann/CPP-Learninng

封装的核心概念

封装是面向对象编程(OOP)的三大特性之一(另外两个是继承和多态),其核心思想是将数据(属性)和操作数据的方法(行为)绑定为一个独立的单元(类),并通过访问控制隐藏内部实现细节,仅对外提供可控的接口。这种设计模式确保了数据的安全性、代码的可维护性和可扩展性。

封装的两层含义:

  1. 数据与行为的结合
    将描述对象状态的成员变量(如圆的半径)和操作这些变量的成员函数(如计算面积)组合在同一个类中,形成逻辑上的整体。例如:

    1
    2
    3
    4
    5
    6
    7
    class Circle {
    private:
    double radius; // 数据成员
    public:
    void setRadius(double r) { radius = r; } // 行为成员
    double getArea() { return 3.14 * radius * radius; }
    };
  2. 访问控制与信息隐藏
    通过访问修饰符(publicprotectedprivate)控制成员的可见性,强制外部代码只能通过公共接口交互,避免直接操作内部数据。例如:

    1
    2
    3
    4
    5
    6
    7
    class Person {
    private:
    int age; // 私有成员,外部无法直接访问
    public:
    void setAge(int a) { if (a > 0) age = a; } // 带验证的公共接口
    int getAge() const { return age; }
    };

访问控制的深度解析

C++提供三种访问修饰符,精确控制类成员的访问范围:

修饰符 类内访问 类外访问 派生类访问
public
protected ✅(仅限子类)
private

实践建议:

  • 默认私有原则:所有成员默认设为private,仅将必要的接口暴露为public
  • 数据私有化:成员变量必须为private,通过getter/setter控制访问,确保数据合法性。
  • 审慎使用protected:仅在明确需要子类直接访问时使用,避免破坏封装。

示例:通过访问控制实现读写权限细分

1
2
3
4
5
6
7
8
9
10
11
class Person {
public:
void setName(const string& n) { name = n; } // 可写
string getName() const { return name; } // 可读
int getAge() const { return age; } // 只读
void setPassword(const string& p) { password = p; } // 只写
private:
string name;
int age = 0;
string password;
};

封装的关键技术实现

类的声明与实现分离

将类的声明(接口)放在头文件(.h),实现(细节)放在源文件(.cpp),降低编译依赖。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Person.h
class Person {
public:
void setName(const string& n);
string getName() const;
private:
string name;
};

// Person.cpp
#include "Person.h"
void Person::setName(const string& n) { name = n; }
string Person::getName() const { return name; }
友元函数与友元类

允许特定外部函数或类访问私有成员,需谨慎使用以避免破坏封装。

1
2
3
4
5
6
7
8
9
class Rectangle {
private:
int width, height;
friend int getArea(const Rectangle& r); // 声明友元函数
};

int getArea(const Rectangle& r) {
return r.width * r.height; // 可访问私有成员
}
RAII(资源获取即初始化)

通过构造函数获取资源,析构函数释放资源,确保异常安全。例如:

1
2
3
4
5
6
7
8
9
10
class FileHandle {
public:
FileHandle(const string& filename) {
file = fopen(filename.c_str(), "r");
if (!file) throw runtime_error("Open failed");
}
~FileHandle() { if (file) fclose(file); } // 自动释放资源
private:
FILE* file;
};

高级封装技巧

Pimpl Idiom(指针指向实现)

将私有成员隐藏在实现类中,头文件仅保留指针,减少编译依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Widget.h(接口类)
class Widget {
public:
Widget();
~Widget();
void doSomething();
private:
class Impl; // 前向声明
unique_ptr<Impl> pImpl; // 指向实现的指针
};

// Widget.cpp(实现类)
class Widget::Impl {
public:
int data;
void process() { /* 具体实现 */ }
};

Widget::Widget() : pImpl(make_unique<Impl>()) {}
Widget::~Widget() = default;
void Widget::doSomething() { pImpl->process(); }
委托构造函数(C++11)

构造函数间相互调用,简化代码:

1
2
3
4
5
6
7
8
class Employee {
public:
Employee(string n, int a) : name(n), age(a) { /* 初始化 */ }
Employee(string n) : Employee(n, 0) { /* 委托给另一个构造函数 */ }
private:
string name;
int age;
};