C++获取private的变量-偷走private
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的更多相关文章
- 使用typeid(变量或类型).name()来获取常量或变量的类型---gyy整理
使用typeid(变量或类型).name()来获取常量或变量的类型 <typeinfo> 该头文件包含运行时类型识别(在执行时确定数据类型)的类 typeid的使用 typeid操作 ...
- 获取Field成员变量类
位于java.lang.reflect.Field包中 getModifiers() 成员变量修饰符(public.private) getName() 成员变量名字 getType() 成员变量类型 ...
- Path形状获取字符串型变量数据
Path形状获取字符串型变量数据: var path = new Path(); path.Data = Geometry.Parse("M 100,200 C 100,25 400,350 ...
- mysql 获取设置环境变量
mysql 获取环境变量 show global variables; 获取指定环境变量 show global variables like '%timeout'; 设置环境变量 set globa ...
- GetEnvironmentVariable 获取常用系统变量(转)
源:GetEnvironmentVariable 获取常用系统变量 //譬如 WINDIR 表示系统目录系统变量, 以这样获: var s:string; begin s:=GetEnvironmen ...
- 通过System获取java环境变量的路径
通过System获取java环境变量的路径代码为: import java.io.FileNotFoundException; import java.io.FileOutputStream; imp ...
- 在js中,ajax放在for中,ajax获取得到的变量有误
先看代码 for(var i=0;i<tds.length;i++){ mui.ajax(url+'api/client/gifts/isSigned', {data :{ sqId:" ...
- 在c代码中获取用户环境变量
1 extern char ** environ 这是一个字符串数组,最后一个元素是null,即\0. 2 在代码中的使用方法 直接extern char **environ,然后 直接environ ...
- 正则表达式 js 怎么获取匹配的变量
正则表达式 js 怎么获取匹配的变量 $0 ~ $9 refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!
随机推荐
- 20145202马超 2016-2017-2 《Java程序设计》第9周学习总结
20145202马超 2016-2017-2 <Java程序设计>第9周学习总结 教材学习内容总结 JDBC 数据库本身是个独立运行的应用程序 撰写应用程序是利用通信协议对数据库进行指令交 ...
- Node.js 从入门到茫然系列——入门篇
在创建服务的时候,我们一般代码就是: var http = require("http"); var server = http.createServer(function(req ...
- Object C学习笔记10-静态方法和静态属性
在.NET中我们静态使用的关键字static有着举足轻重的作用,static 方法可以不用实例化类实例就可以直接调用,static 属性也是如此.在Object C中也存在static关键字,今天的学 ...
- UGUI实现不规则区域点击响应
UGUI实现不规则区域点击响应 前言 大家吼啊!最近工作上事情特别多,没怎么打理博客.今天无意打开cnblog才想起该写点东西了.今天给大家讲一个Unity中不规则区域点击响应的实现方法,使用UGUI ...
- 在Visual Studio中使用.lib和.dll的环境搭建
1 静态库和动态链接库的区别 动态链接库是在运行的时候被调用的,静态库在链接的时候被链接到最终生成的应用程序(.exe)中 静态库需要用到的文件 (.lib .h) 头文件(.h)提供接口,库文件(. ...
- springboot+elasticsearch实现全局检索
ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Apach ...
- shell小记
1.以#!/bin/bash 开头2.执行方式 bash sh ./ `script`3.变量: 系统变量 自定义变量 --->export 可导出为全局环境变量 set显示所 ...
- 正确配置 debian squeeze apt 源
本想在 Debian Squeeze 上安装一些依赖,没想到刚执行 apt-get update ,就出现这样的错误信息. W: GPG error: http://mirrors.163.com s ...
- python初学者随笔Week2
一.集合 去重,把一个列表变成集合,自动去重 关系测试,测试两组数据的交集,并集,差集关系 集合是无序的 集合的操作: list_1 = [1,4,5,7,3,6,7,9] list_1 = set( ...
- Firefox浏览器【书签工具栏】里的网址链接无法删除的解决办法
今天使用Firefox浏览器,发现有一些我从来都没有访问的网站出现在[书签工具栏], 也不知道是什么原因被添加进来的(可能是安装某个插件被插的),于是点删除,发现还删除不了,很是老火,研究了一番,把删 ...