我们都知道,C++ 不允许通过对象来访问 private、protected 属性的成员变量;C++面向对象中有一大特性就是封装,使用不同的访问控制符来控制外界对其的访问权限。
#include<iostream>
using namespace std;
class A{
private://私有
int mPrivate;
int nPrivate;
public:
A():mPrivate(1),nPrivate(2){};
template<typename T> void func(const T &t){}
const int GetValueN(){
return nPrivate;
}
};
不过 C++ 的这种限制仅仅是语法层面的,通过某种“蹩脚”的方法,我们能够突破访问权限的限制,访问到 private、protected 属性的成员变量,赋予我们这种“特异功能”的,正是强大而又灵活的指针(Pointer);
可以通过以下方法突破对private成员的访问权限
1)操作指针修改内存数据,根据偏移(推荐)
在对象的内存模型中,成员变量和对象的开头位置会有一定的距离;和struct结构体中的数据成员偏移量(offset)是一个概念;
一旦知道了对象的起始地址,再加上偏移就能够求得成员变量的地址,知道了成员变量的地址和类型,也就能够轻而易举地知道它的值;
当通过对象指针访问成员变量时,编译器实际上也是使用这种方式来取得它的值;
#include<iostream>
#include "dome.h"
using namespace std;
int main(){
A obj=A();
int temp=3;
int *ptr=(int*)(&obj);
*(ptr+1)=temp;//实际上就是根据偏移量
cout<<"nPrivate="<<obj.GetValueN()<<endl;
//nPrivate=3
return 0;
}
2)使用友元声明(可以是友元函数或者友元类)
#include<iostream>
using namespace std;
class A{
private:
friend void Jack(A &);//友元函数
int mPrivate;
int nPrivate;
public:
A():mPrivate(1),nPrivate(2){}
template<typename T> void fun(const T &t){}
const int GetValueN(){
return nPrivate;
}
};
void Jack(A &a){
a.nPrivate=3;
}
int main(){
A obj=A();
Jack(obj);
cout<<"nPrivate="<<obj.GetValueN()<<endl;
return 0;
}
3)使用模板
类A中定义了一个模板函数func(),在类外加一个模板函数,导致模板推演的过程中多出一个自己写的并且加入了备选组中,相当于多了一个重载。类外函数的参数是在匿名空间中定义的特定类型,所以避免扰乱原本该函数的功能。
#include<iostream>
using namespace std;
class A{
private:
int mPrivate;
int nPrivate;
public:
A():mPrivate(1),nPrivate(2){};
template<typename T> void func(const T &t){}
const int GetValueN(){
return nPrivate;
}
};
namespace {struct B{};}
template<> void A::func(const B&){
nPrivate=3;
}
int main(){
A obj;
obj.func(B());
cout<<"nPrivate="<<obj.GetValueN()<<endl;
return 0;
}
4)使用宏,缺点是造成二义性
#define private public
5)利用指针修改私有成员
#include<iostream>
using namespace std;
class A{
public:
A():i(1){}
void print(){
cout<<"A::i="<<i<<endl;
}
private:
const int i;
};
int main(){
A a;
int* p=(int*)&a; //突破编译器防线
a.print();
*p=30; //修改a对象私有成员的值
a.print();
return 0;
}
C++ 的成员访问权限仅仅是语法层面上的,是指访问权限仅对取成员运算符.和->起作用,而无法防止直接通过指针来访问;
参考博客