访问私有成员——从技术实现的角度破坏"封装" 性

2023-06-13 14:47:40 浏览数 (1)

你好,我是雨乐!

大约是在大二上半学期的时候接触的C语言,在下半学期开始接触C 语言。在C 教材的前几章就介绍了其特性,即:C 是一门面向对象语言,具有封装、继承和多态三个特点。后来,随着编码的增多以及工作经验的积累,对个概念的理解越来越深。编码习惯也严格按照相应的规则,该封装的时候进行封装,该继承的时候进行继承,以使得编程思维从之前的面向过程逐步过渡到面向对象。

作为开发人员,遵循编程规则本来就无可厚非,但是如果大家都遵循规则难免会有创新或者技术进步。有时候,在做某件事或者看到某个实现方案的时候,想想为什么要这么做,有没有更好的实现方案,这个编程或者做事习惯往往使得自己受益匪浅。比如,我们都知道每个线程都有一个自己的栈,线程内的局部变量出了作用域就会被释放,那么有没有可能跨线程从另外一个线程去访问该线程的局部变量呢?其实,问题不算难,我们只需要尝试即可,但往往缺少的就是这种尝试。对于C 三大特性中的封装特性,如果直接访问私有变量,则编译器会报错,那么有没有其它方式可以访问私有变量呢?

今天,不妨试着反其道而行,尝试以其他方式破坏封装性,直接访问私有变量。

从一段代码说起

代码示例如下:

代码语言:javascript复制
#include <iostream>

class A {
 public:
  A() = default;
 private:
  int data_ = 0;
};

int main() {
  A a;
  std::cout << a.data_ << std::endl;

  return 0;
}

在gcc5.4下进行编译,不出所料,编译失败,报错如下:

代码语言:javascript复制
test.cc: 在函数‘int main()’中:
test.cc:7:15: 错误:‘int A::data_’是私有的
   int data_ = 0;

从报错信息看,因为data_成员变量是私有的,而通过对象访问私有成员变量是不被允许的,除了通过重新定义一个公共接口,在该接口内对data_进行访问外,但是这种方式并没有实现本文的目的即破坏封装性,那么有没有其它方式呢?

第一次尝试

c 标准中有这样一段描述:

The usual access checking rules do not apply to non-dependent names used to specify template arguments of the simple-template-id of the partial specialization. [ Note: The template arguments may be private types or objects that would normally not be accessible. Dependent names cannot be checked when declaring the partial specialization, but will be checked when substituting into the partial specialization. — end note ]

也就是说模板参数可以是某个类的私有类型,所以,我们可以借助此条标准继续实现我们的目的,代码如下:

代码语言:javascript复制
#include <iostream>

class A {
 public:
  A() = default;
 private:
  int data_ = 0;
};

template<int A::*Member>
int& GetPrivateData(A& obj) {
  return obj.*Member;
}

template int& GetPrivateData<&A::data_>(A&);

int main() {
  A obj;
  GetPrivateData<&A::data>(obj);

  return 0;
}

在上述代码中,定义了一个函数模板,其模板参数为int A::*Member,功能是返回类A中的成员变量,编译后,报错如下:

代码语言:javascript复制
test.cc: 在函数‘int main()’中:
test.cc:7:15: 错误:‘int A::data_’是私有的
   int data_ = 0;
               ^
test.cc:22:3: 错误:在此上下文中
   GetPrivateData<&A::data_>(obj);

看来此方式还是行不通,只能另想它法。

第二次尝试

在上面的提示中,显示不能直接访问私有成员,标准提供了个方法,就是将需要访问类私有成员的函数或者类声明为friend。看到这块,你可能会想,有了friend用得着你教?

0 人点赞