private提供了对数据的封装,使得private成员只能被类自身的成员函数以及类的友元访问,其他的函数或者类想要访问private成员只能通过该类所提供的set和get的方法进行访问,

或者返回其指针或引用(effective C++中提到过要避免返回对象内部构件的引用,指针,或迭代器。这样会提高封装性,帮助 const 成员函数产生 const 效果,并将悬空句柄产生的可能性降到最低,所以但这个方法并不是特别的好)

但如果你想获得一个类的private成员,但是该类的已经在项目被大量的使用,或者是因为其他的原因,你没有办法添加get和set方法时,又应该如何获得该类的private成员呢?

我总结出了以下几种方法

方法一:重定义

#define  private public

示例

A.h定义

#pragma once
class A
{
int j;
public:
A() :i(), j(2)
{ }
template<class Type>
void show(Type t)
{
cout << "Hello" << endl;
}
private:
int i;
};

main.cpp

#include <iostream>
#define private public
#include "A.h"
using namespace std; int main()
{
A a;
cout << a.i << endl;
//cout<<a.j<<endl;无法获得
system("pause");
return ;
}

该方法的优点的是简单但也有不少的缺点

1.如果在类的定义时不指定访问标号关键字(public,protected,private),使用默认的private访问限制,那么该方法就无法达到目的了,比如这里的j就无法获得

2.降低代码的可读性,改变的一个关键字的意义,没有注意到这一点的程序员会产生困扰

3.将所有使用了private访问的标号的成员的访问等级都变成了public,降低了数据的封装性

方法二:模拟内存法

A.h定义

#include <iostream>
#include "A.h"
using namespace std; int main()
{
A a;
void *p = &a;
cout << "j:" << *(int*)p << endl;
cout << "i:" << *((int*)p+)<< endl;// *(int*)((char*)p+4)
system("pause");
return ;
}

C++标准中要求,在同一个访问区域中,成员的排列只需符合较晚出现的成员在类的成员中有较高的地址即可,成员之间可能会因为数据对齐所需,添加一些字节

目前各编译器都是吧一个以上的访问区域连在一起,安装声明的顺序成为一个连续的区域

所以类A的一个对象的内存布局类似于这样:

指针p指向j,将p加上一个int长度或者4个char长度就可以指向i了

但这个方法的缺点也很明显,需要程序员自己对类的内存布局有着较强的了解,考虑到数据对齐,虚函数,不同编译器的实现等等方面

比如以下两种情况

1、数据对齐

A.h定义

#pragma once
class A
{
char j;
public:
A() :i(), j()
{ }
template<class Type>
void show(Type t)
{
cout << "Hello" << endl;
}
private:
int i; };

char j占用了一个byte,而i为了数据对齐,在内存布局上并不是与j紧挨着的,而是隔了3个byte,

所以获得i和j的间隔与上一个一样,只是j的类型变了

#include <iostream>
#include "A.h"
using namespace std; int main()
{
A a;
void *p = &a;
cout << "j:" << *((char*)p) << endl;
cout << "i:" << *((int*)p+)<< endl;
system("pause");
return ;
}

2.加入虚函数

A.h定义

#pragma once
class A
{
int j;
public:
A() :i(), j()
{ }
virtual void show()
{ }
template<class Type>
void show(Type t)
{
cout << "Hello" << endl;
}
private:
int i; };

编译器为了支持虚函数,会在类的每一个对象中,产生一个额外的虚函数指针指向相应的虚函数表,不同的编译器对这个指针处理不同,有点将它放在了类对象的尾端,有的将它放在了类对象的开始处

vs2013将它放在了类的开头处

所以类A的一个对象的内存布局应该类似于这样:

需要将p加上4个字节后才能指向j

#include <iostream>
#include "A.h"
using namespace std; int main()
{
A a;
void *p = &a;
cout << "j:" << *((int*)p+) << endl;
cout << "i:" << *((int*)p+)<< endl;
system("pause");
return ;
}

所以如果虚函数过多,又加入了虚继承, 类里面又有大量程序员自己定义的类型,那么该方法就会很麻烦了。

方法三:李代桃僵

A.h的定义

#pragma once
class A
{
char j;
public:
A() :i(), j('b')
{ }
virtual void show()
{ }
template<class Type>
void show(Type t)
{
cout << "Hello" << endl;
}
private:
int i; };

李代桃僵法是模拟内存布局的另一个实现方式

我们看到现在A里有一个虚函数,一个j和一个i

如果直接使用模拟内存法的话会很麻烦

所以我们可以另声明一个对象B,它的内存布局和A的一样,只是i和j的访问限制变成了public

这样我们可以把一个指向A的对象的指针当做一个指向B的对象指针来使用

#include <iostream>
#include "A.h"
using namespace std; class B
{
public:
char j;
public:
B() :i(), j('b')
{ }
virtual void show()
{ }
/*template<class Type>
void show(Type t)
{
cout << "Hello" << endl;
}*/
public:
int i; };
int main()
{
A a;
B *b = (B*)&a;
cout<<"j:" << b->j << endl;
cout<<"i:" << b->i << endl;
system("pause");
return ;
}

非虚成员函数show放在函数段中,并不在类对象的布局中占用空间,所以有没有show函数都可以

因为B的对象的内存布局与A一样,只是访问限制不同,所以可以利用对B对象的规则去访问A的对象

一个指向B对象的指针实际指向了一个A对象,对B中j和i的访问实际上是对A对象中i和j的访问

该方法模拟内存法容易了很多,但你需要额外声明一个B对象的定义,而且必须要确保B对象的内存布局要与A对象的一致

方法四 特化函数模板法

a.h的定义

#pragma once
class A
{
char j;
public:
A() :i(), j('b')
{ }
virtual void show()
{ }
template<class Type>
void show(Type t)
{
cout << "Hello" << endl;
}
private:
int i; };

这里我们发现A有个函数模板show,所以我们可以利用对函数模板show进行特化的方式合法的获得i和j的public访问权限

#include <iostream>
#include "A.h"
using namespace std; class B
{ };
template<>
void A::show(B b)
{
cout << "j:"<<this->j << endl;
cout << "i:" << this->i << endl;
}
int main()
{
A a;
a.show(B());
system("pause");
return ;
}

该方法合理,简单,但也有缺点就是相应的类必须要有成员模板,并且该模板的访问限制为public才可以

总结

方法 优点 缺点 可移植性
重定义 简单

1.如果在类的定义时不指定访问标号关键字(public,protected,private),使用默认的private访问限制,那么该方法就无法达到目的了,比如这里的j就无法获得

2.降低代码的可读性,改变的一个关键字的意义,会没有注意到这一点的程序员照成困扰

3.将所有使用了private访问的标号的成员的访问等级都变成了public,降低了数据的封装性

模拟内存法 虚函数过多,又加入了虚继承, 类里面又有大量程序员自己定义的类型时,那么该方法就会很麻烦了。需要程序员对内存布局有较深的认识
李代桃僵 简单,可能在有些人看来比较清楚 需要额外声明一个B对象的定义,而且必须要确保B对象的内存布局要与想要访问的A对象的一致
特化函数模板法 合理,简单 相应的类必须要有成员模板,并且该模板的访问限制为public才可以

C++获取private的变量-偷走private的更多相关文章

  1. 使用typeid(变量或类型).name()来获取常量或变量的类型---gyy整理

    使用typeid(变量或类型).name()来获取常量或变量的类型 <typeinfo>  该头文件包含运行时类型识别(在执行时确定数据类型)的类 typeid的使用   typeid操作 ...

  2. 获取Field成员变量类

    位于java.lang.reflect.Field包中 getModifiers() 成员变量修饰符(public.private) getName() 成员变量名字 getType() 成员变量类型 ...

  3. Path形状获取字符串型变量数据

    Path形状获取字符串型变量数据: var path = new Path(); path.Data = Geometry.Parse("M 100,200 C 100,25 400,350 ...

  4. mysql 获取设置环境变量

    mysql 获取环境变量 show global variables; 获取指定环境变量 show global variables like '%timeout'; 设置环境变量 set globa ...

  5. GetEnvironmentVariable 获取常用系统变量(转)

    源:GetEnvironmentVariable 获取常用系统变量 //譬如 WINDIR 表示系统目录系统变量, 以这样获: var s:string; begin s:=GetEnvironmen ...

  6. 通过System获取java环境变量的路径

    通过System获取java环境变量的路径代码为: import java.io.FileNotFoundException; import java.io.FileOutputStream; imp ...

  7. 在js中,ajax放在for中,ajax获取得到的变量有误

    先看代码 for(var i=0;i<tds.length;i++){ mui.ajax(url+'api/client/gifts/isSigned', {data :{ sqId:" ...

  8. 在c代码中获取用户环境变量

    1 extern char ** environ 这是一个字符串数组,最后一个元素是null,即\0. 2 在代码中的使用方法 直接extern char **environ,然后 直接environ ...

  9. 正则表达式 js 怎么获取匹配的变量

    正则表达式 js 怎么获取匹配的变量 $0 ~ $9 refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!

随机推荐

  1. 洛谷 P1762 偶数

    洛谷 P1762 偶数 题目描述 给定一个正整数n,请输出杨辉三角形前n行的偶数个数对1000003取模后的结果. 输入输出格式 输入格式: 一个数 输出格式: 结果 输入输出样例 输入样例#1:   ...

  2. HBase数据模型的一些概念

    首先来先理解一个概念:HBase是一种列式存储的分布式数据库. 表              在HBase中数据以表的形式存储.使用表的主要原因是把某些列组织起来一起访问,同一个表中的数据通常是相关的 ...

  3. 【SDOI2011】工作安排

    题面 题解 如果没有分段函数的限制的话就很好做了 但是我们发现分段函数的段很少,我们就可以将每一段拆开, 强制限制一定流量就可以了 代码 #include<cstdio> #include ...

  4. DELL R710使用4T硬盘亮黄灯

    事件背景 公司DELL R710的物理机上面运行的SQL SERVER数据库,因存储空间不足需要扩充空间.现系统盘(300G SAS 6Gbps 15K*2)RAID 1,数据盘(500G SAS 6 ...

  5. CF97C Winning Strategy

    CF97C Winning Strategy 洛咕(题意应该快传上去了) 这题好玄学鸭...都不知道为啥是对的 设\(f[i][j]\)表示打了i轮比赛,有j个参加了一次的人,直接枚举有几个参加了转移 ...

  6. 一目了然带你看lniux

    第一部分    熟悉Linux基本操作 一.初识Linux 1.Linux特点 ◊  开放性 ◊  多用户 ◊  多任务 ◊  良好的用户界面 ◊  设备独立性 ◊  丰富的网络功能 ◊  可靠的系统 ...

  7. 【Python学习笔记之三】lambda表达式用法小结

    除了def语句之外,Python还提供了一种生成函数对象的表达式形式.由于它与LISP语言中的一个工具很相似,所以称为lambda.就像def一样,这个表达式创建了一个之后能够调用的函数,但是它返回了 ...

  8. 并发系列(四)-----CAS

    一 简介 保证Java中的原子操做方式有两种方式  1 加锁(可以理解悲观锁机制)  2 CAS(可以理解为乐观锁机制)  CAS全称是Compare and Swap 即比较并替换.在JDK中许多地 ...

  9. 03-matplotlib-折线图

    import numpy as np import matplotlib.pyplot as plt import matplotlib.dates as mdates ''' 折线图,用直线段将各数 ...

  10. Sony深度学习框架 - Neural Network Console - 教程(1)- 原来深度学习可以如此简单

    “什么情况!?居然不是黑色背景+白色文字的命令行.对,今天要介绍的是一个拥有白嫩的用户界面的深度学习框架.” 人工智能.神经网络.深度学习,这些概念近年已经涌入每个人的生活中,我想很多人早就按捺不住想 ...