源起
最近写代码的时候,碰到了一个PCL的bug,本想通过继承的方式把这个bug fix掉,但是无奈我必须访问基类的私有成员函数和私有成员变量。正常情况下,我是不可能从外部访问到一个类的私有成员变量和私有成员函数的,但是总有Tricky的方法。经过Google,找到了下面这么几个方法。
首先看我们的这个代码。
class A{
private:
int v;
void f();
public:
A() {}
};
void A::f() {
std::cout << "private f function" << std::endl;
}
假设我们有A
这个类,这个类有一个私有成员变量v
和一个私有成员函数f
,现在我们要读写v
或是调用函数f
。
宏定义Hack-private
变public
我们可以用宏定义的Hack方法改变函数A
内部函数、变量的访问权限。
#define private public
#define protected public
class A{
private:
int v;
void f();
public:
A() {}
};
#undef private
#undef protected
void A::f() {
std::cout << "private f function" << std::endl;
}
int main() {
A a;
a.v = 10;
a.f();
return 0;
}
缺点
- 这种Hack方式比较暴力,完全改变了类的访问限定。
这种对类的默认访问限定不起作用,比如:
class A { int v; void f(); public: A() {} };
类
A
的变量v
和函数f
没有显式的访问限定,默认都是私有的。这种是没办法处理的。
对于只有头文件和lib库的访问限定Hack也是有用的,只需要在包含的头文件前后加上
#define
和#undef
就行了。#define private public #include "a.h" #undef private // 再包含其他的头文件。。
也可以将private限定变更为protected,然后以继承的方式同样也可以访问到原先私有的成员函数和成员变量
宏定义Hack-友元函数、友元类
在上面直接改变类的访问限定的方法上,可以发展出来另一种更好一些的方法——友元类。这种方法不改变外部代码对于类的访问权限,只有某个类才能访问到所有成员变量和成员函数。
class Hack;
#define private friend class Hack; private
#define public friend class Hack; public
#define protected friend class Hack; protected
class A {
private:
int v;
void f();
public:
A() {}
};
#undef private
#undef public
#undef protected
void A::f() {
std::cout << "private f function" << std::endl;
}
class Hack {
public:
static void set_v(A& a, int val) {
a.v = val;
}
static void func_f(A& a) {
a.f();
}
};
int main() {
A a;
Hack::set_v(a, 10);
Hack::func_f(a);
return 0;
}
这种方式就相对好一些了,其他代码也没有办法访问A的私有成员函数和成员变量,所有的操作必须通过Hack这个类完成。而且对于没有显式private
的也是有用的。
同样,对于只有头文件和lib库的访问限定Hack也是有用的,只需要在包含的头文件前后加上
#define
和#undef
就行了。#define private friend class Hack; private #define public friend class Hack; public #define protected friend class Hack; protected #include "a.h" #undef private #undef public #undef protected // 再包含其他的头文件。。
类的强制转换Hack
先说方法,请看代码。
#include <iostream>
class A{
private:
int v;
void f();
public:
A() {}
void print();
};
void A::f() {
std::cout << "private f function" << std::endl;
}
void A::print() {
std::cout << v << std::endl;
}
class Hack {
public:
int v;
};
int main() {
A a;
Hack *ptr = (Hack *)&a;
ptr->v = 100;
a.print();
return 0;
}
注意
- 这种方式不值得推荐,相当于直接在内存层面去操作了,而且当一个类的成员变量有几十个甚至是上百个的时候,这种方式就非常容易出错。
- 这种方式不能调用私有成员函数
优雅的强制类型变换Hack
#include <iostream>
class A{
private:
int v;
void f();
public:
A() {}
void print();
};
void A::f() {
std::cout << "private f function" << std::endl;
}
void A::print() {
std::cout << v << std::endl;
}
template<typename Tag>
struct result {
/* export it ... */
typedef typename Tag::type type;
static type ptr;
};
template<typename Tag>
typename result<Tag>::type result<Tag>::ptr;
template<typename Tag, typename Tag::type p>
struct rob : result<Tag> {
/* fill it ... */
struct filler {
filler() { result<Tag>::ptr = p; }
};
static filler filler_obj;
};
template<typename Tag, typename Tag::type p>
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;
// 成员函数
struct Af { typedef void(A::*type)(); };
template class rob<Af, &A::f>;
// 成员变量
struct Av { typedef int A::*type; };
template class rob<Av, &A::v>;
int main() {
A a;
(a.*result<Af>::ptr)();
(a.*result<Av>::ptr) = 100;
a.print();
return 0;
}
这个方法就比较优雅了,即可以访问私有成员函数,又可以访问私有成员变量。
这个方法只可以在gcc、clang上使用,vc++会报'A::f': cannot access private member declared in class 'A'
这样的错误