C++系列总结——封装

时间:2023-12-04 18:30:02

前言

众所周知,封装、继承和多态是面向对象编程的三大特性。C++作为一门面向对象的编程语言,自然支持了这些特性,但C++是如何实现这些特性的呢?今天先说下我理解的封装。

封装

通常我们会把下面的行为也叫封装,但面向对象的封装并不只是把函数或类型包裹在一起,更重要的是给这些函数或类型设置访问权限

  • 将一堆类型变量用一个结构体包裹起来,对外只有一个结构体名
  • 将一堆过程用一个函数包裹起来,对外只有一个函数名

C++提供了三种权限

  • public:任何类都能可访问
  • protected:只有本类或本类的派生类能访问
  • private:只有本类能访问

    类指classstruct,并不单指classclassstruct只有默认访问权限的区别
    友元类和友元函数能访问该类的任何东西

访问权限只在编译阶段检查,而且检查的依据只是当前的类声明,举个例子来说明

  • 执行g++ student.cpp -fPIC -shared -o liba.so将以下代码编译为动态库,提供给其他人使用。
// student.h
class Student
{
public:
    Student();
private:
    int age;
};

// student.cpp
#include "student.h"
Student::Student()
{
    age = 10;
}
  • 篡改student.h,将所有成员变量的访问权限都设为public
class Student
{
public:
    Student();
    int age;
};
  • g++ main.cpp -L./ -la编译没有报错。——从此就可以看出“权限检查的依据只是当前的类声明”
// main.cpp
#include "student.h"
int main()
{
    Student s;
    s.age = 40; // 肆意变更自己的年纪
    return 0;
}

当然可以采用如下的方式来隐藏内部成员变量细节,这样即使调用者修改了类声明里的访问权限,也不知道为了来达到自己的目的该如何修改。

// student.h
class Student
{
public:
    Student();
private:
    struct Internal;
    Internal* in;
};

此外,这还带来一个好处就是内部成员变量的修改,调用者完全无感知,不需要更新头文件了(我想这才是使用这种方式的主要原因)。当然这仍然无法阻止别人猜测出数据结构后,直接使用成员变量地址去读写。个人觉得只有提供给第三方或第二方的类才需要使用这种方式(内部还是可以信任的)。如果所有类都这样实现的话,可能就会导致内存碎片了。

结语

我觉得封装最大的意义还是在于让调用者不要关注自己无权访问的内容(忽略细节),只需要基于自己可访问的部分(基于接口)去实现业务。C是一门面向过程的语言,它是没有访问权限这一说的,调用者无法知道哪些是自己无权访问的,编译器也无法提醒越权访问的错误,C++则提供了这种编程约束