C++ 我可以在不使用朋友的情况下从课堂外访问私人成员吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/424104/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can I access private members from outside the class without using friends?
提问by Jason Baker
Disclaimer
免责声明
Yes, I am fully aware that what I am asking about is totally stupid and that anyone who would wish to try such a thing in production code should be fired and/or shot. I'm mainly looking to see if canbe done.
是的,我完全意识到我所问的完全是愚蠢的,任何想在生产代码中尝试这种事情的人都应该被解雇和/或开枪。我主要是想看看能不能做。
Now that that's out of the way, is there any way to access private class members in C++ from outside the class? For example, is there any way to do this with pointer offsets?
既然这样,有没有办法从类外部访问 C++ 中的私有类成员?例如,有没有办法用指针偏移来做到这一点?
(Naive and otherwise non-production-ready techniques welcome)
(欢迎天真和其他非生产就绪技术)
Update
更新
As noted in the comments, I asked this question because I wanted to write a blog post on over-encapsulation (and how it affects TDD). I wanted to see if there was a way to say "using private variables isn't a 100% reliable way to enforce encapsulation, even in C++." At the end, I decided to focus more on how to solve the problem rather than why it's a problem, so I didn't feature some of the stuff brought up here as prominently as I had planned, but I still left a link.
正如评论中所指出的,我问这个问题是因为我想写一篇关于过度封装(以及它如何影响 TDD)的博客文章。我想看看是否有一种方法可以说“即使在 C++ 中,使用私有变量也不是 100% 可靠的强制封装方式”。最后,我决定更多地关注如何解决问题而不是为什么会出现问题,所以我没有像我计划的那样突出这里提出的一些东西,但我仍然留下了一个链接。
At any rate, if anyone's interested in how it came out, here it is: Enemies of Test Driven Development part I: encapsulation(I suggest reading it before you decide that I'm crazy).
无论如何,如果有人对它的结果感兴趣,这里是: 测试驱动开发的敌人第一部分:封装(我建议在你认为我疯了之前先阅读它)。
回答by dalle
If the class contains any template member functions you can specialize that member function to suit your needs. Even if the original developer didn't think of it.
如果该类包含任何模板成员函数,您可以专门化该成员函数以满足您的需要。就算原开发者没有想到。
safe.h
安全文件
class safe
{
int money;
public:
safe()
: money(1000000)
{
}
template <typename T>
void backdoor()
{
// Do some stuff.
}
};
main.cpp:
主.cpp:
#include <safe.h>
#include <iostream>
class key;
template <>
void safe::backdoor<key>()
{
// My specialization.
money -= 100000;
std::cout << money << "\n";
}
int main()
{
safe s;
s.backdoor<key>();
s.backdoor<key>();
}
Output:
输出:
900000
800000
回答by Johannes Schaub - litb
I've added an entry to my blog(see below) that shows how it can be done. Here is an example on how you use it for the following class
我在我的博客中添加了一个条目(见下文),展示了它是如何完成的。这是一个关于如何在以下课程中使用它的示例
struct A {
private:
int member;
};
Just declare a struct for it where you describe it and instantiate the implementation class used for robbery
只需在描述它的地方为其声明一个结构体并实例化用于抢劫的实现类
// tag used to access A::member
struct A_member {
typedef int A::*type;
friend type get(A_member);
};
template struct Rob<A_member, &A::member>;
int main() {
A a;
a.*get(A_member()) = 42; // write 42 to it
std::cout << "proof: " << a.*get(A_member()) << std::endl;
}
The Rob
class template is defined like this, and needs only be defined once, regardless how many private members you plan to access
在Rob
类模板的定义如下,并且仅需要定义一次,不管有多少私有成员计划访问
template<typename Tag, typename Tag::type M>
struct Rob {
friend typename Tag::type get(Tag) {
return M;
}
};
However, this doesn't show that c++'s access rules aren't reliable. The language rules are designed to protect against accidental mistakes - if you try to rob data of an object, the language by-designdoes not take long ways to prevent you.
但是,这并不表明 c++ 的访问规则不可靠。语言规则旨在防止意外错误 - 如果您试图窃取对象的数据,语言的设计不会采取很长的方法来阻止您。
回答by ChrisW
The following is sneaky, illegal, compiler-dependent, and may not work depending on various implementation details.
以下是偷偷摸摸的、非法的、依赖于编译器的,并且可能无法工作,具体取决于各种实现细节。
#define private public
#define class struct
But it is an answer to your OP, in which you explicitly invite a technique which, and I quote, is "totally stupid and that anyone who would wish to try such a thing in production code should be fired and/or shot".
但这是对您的 OP 的回答,您在其中明确邀请了一种技术,我引用该技术“完全愚蠢,任何想在生产代码中尝试这种事情的人都应该被解雇和/或枪杀”。
Another technique is to access private member data, by contructing pointers using hard-coded/hand-coded offsets from the beginning of the object.
另一种技术是通过使用从对象开头开始的硬编码/手工编码偏移量构造指针来访问私有成员数据。
回答by SmacL
Hmmm, don't know if this would work, but might be worth a try. Create another class with the same layout as the object with private members but with private changed to public. Create a variable of pointer to this class. Use a simple cast to point this to your object with private members and try calling a private function.
嗯,不知道这是否可行,但可能值得一试。创建另一个与具有私有成员但私有更改为公共的对象具有相同布局的类。创建一个指向此类的指针变量。使用简单的强制转换将其指向具有私有成员的对象并尝试调用私有函数。
Expect sparks and maybe a crash ;)
期待火花,也许是崩溃;)
回答by Rob K
class A
{
int a;
}
class B
{
public:
int b;
}
union
{
A a;
B b;
};
That should do it.
那应该这样做。
ETA: It will work for this sort of trivial class, but as a general thing it won't.
ETA:它适用于这种琐碎的课程,但一般来说它不会。
TC++PL Section C.8.3: "A class with a constructor, destructor, or copy operation cannot be the type of a union member ... because the compiler would not know which member to destroy."
TC++PL 第 C.8.3 节:“具有构造函数、析构函数或复制操作的类不能是联合成员的类型……因为编译器不知道要销毁哪个成员。”
So we're left with the best bet being to declare class B
to match A
's layout and hack to look at a class's privates.
因此,我们最好的选择是声明class B
matchA
的布局并破解以查看类的私有。
回答by Martin York
If you can get a pointer to a member of a class you can use the pointer no matter what the access specifiers are (even methods).
如果您可以获得指向类成员的指针,则无论访问说明符是什么(甚至方法),您都可以使用该指针。
class X;
typedef void (X::*METHOD)(int);
class X
{
private:
void test(int) {}
public:
METHOD getMethod() { return &X::test;}
};
int main()
{
X x;
METHOD m = x.getMethod();
X y;
(y.*m)(5);
}
Of course my favorite little hack is the friend template back door.
当然,我最喜欢的小技巧是朋友模板后门。
class Z
{
public:
template<typename X>
void backDoor(X const& p);
private:
int x;
int y;
};
Assuming the creator of the above has defined backDoor for his normal uses. But you want to access the object and look at the private member variables. Even if the above class has been compiled into a static library you can add your own template specialization for backDoor and thus access the members.
假设上面的创建者已经为他的正常使用定义了 backDoor。但是您想访问该对象并查看私有成员变量。即使上面的类已经编译成静态库,您也可以为 backDoor 添加自己的模板特化,从而访问成员。
namespace
{
// Make this inside an anonymous namespace so
// that it does not clash with any real types.
class Y{};
}
// Now do a template specialization for the method.
template<>
void Z::backDoor<Y>(Y const& p)
{
// I now have access to the private members of Z
}
int main()
{
Z z; // Your object Z
// Use the Y object to carry the payload into the method.
z.backDoor(Y());
}
回答by JaredPar
It's definately possible to access private members with a pointer offset in C++. Lets assume i had the following type definition that I wanted access to.
绝对可以在 C++ 中使用指针偏移访问私有成员。让我们假设我有以下我想要访问的类型定义。
class Bar {
SomeOtherType _m1;
int _m2;
};
Assuming there are no virtual methods in Bar, The easy case is _m1. Members in C++ are stored as offsets of the memory location of the object. The first object is at offset 0, the second object at offset of sizeof(first member), etc ...
假设 Bar 中没有虚方法,最简单的情况是 _m1。C++ 中的成员存储为对象内存位置的偏移量。第一个对象位于偏移量 0 处,第二个对象位于 sizeof(first member) 偏移量处,依此类推...
So here is a way to access _m1.
所以这里有一种访问_m1的方法。
SomeOtherType& GetM1(Bar* pBar) {
return*(reinterpret_cast<SomeOtherType*>(pBar));
}
Now _m2 is a bit more difficult. We need to move the original pointer sizeof(SomeOtherType) bytes from the original. The cast to char is to ensure that I am incrementing in a byte offset
现在 _m2 有点困难。我们需要从原始指针中移动原始指针 sizeof(SomeOtherType) 字节。转换为 char 是为了确保我以字节偏移量递增
int& GetM2(Bar* pBar) {
char* p = reinterpret_cast<char*>(pBar);
p += sizeof(SomeOtherType);
return *(reinterpret_cast<int*>(p));
}
回答by Sushant Mahajan
cool question btw... here's my piece:
顺便说一句很酷的问题......这是我的作品:
using namespace std;
class Test
{
private:
int accessInt;
string accessString;
public:
Test(int accessInt,string accessString)
{
Test::accessInt=accessInt;
Test::accessString=accessString;
}
};
int main(int argnum,char **args)
{
int x;
string xyz;
Test obj(1,"Shit... This works!");
x=((int *)(&obj))[0];
xyz=((string *)(&obj))[1];
cout<<x<<endl<<xyz<<endl;
return 0;
}
Hope this helps.
希望这可以帮助。
回答by iammilind
This answer is based on the exact concept demonstrated by @Johannes's answer/blog, as that seems to be the only "legitimate" way. I have converted that example code into a handy utility. It's easily compatible with C++03 (by implementing std::remove_reference
& replacing nullptr
).
这个答案基于@Johannes 的答案/博客所展示的确切概念,因为这似乎是唯一的“合法”方式。我已将该示例代码转换为一个方便的实用程序。它很容易与 C++03 兼容(通过实现std::remove_reference
& 替换nullptr
)。
Library
图书馆
#define CONCATE_(X, Y) X##Y
#define CONCATE(X, Y) CONCATE_(X, Y)
#define ALLOW_ACCESS(CLASS, MEMBER, ...) \
template<typename Only, __VA_ARGS__ CLASS::*Member> \
struct CONCATE(MEMBER, __LINE__) { friend __VA_ARGS__ CLASS::*Access(Only*) { return Member; } }; \
template<typename> struct Only_##MEMBER; \
template<> struct Only_##MEMBER<CLASS> { friend __VA_ARGS__ CLASS::*Access(Only_##MEMBER<CLASS>*); }; \
template struct CONCATE(MEMBER, __LINE__)<Only_##MEMBER<CLASS>, &CLASS::MEMBER>
#define ACCESS(OBJECT, MEMBER) \
(OBJECT).*Access((Only_##MEMBER<std::remove_reference<decltype(OBJECT)>::type>*)nullptr)
API
应用程序接口
ALLOW_ACCESS(<class>, <member>, <type>);
Usage
用法
ACCESS(<object>, <member>) = <value>; // 1
auto& ref = ACCESS(<object>, <member>); // 2
Demo
演示
struct X {
int get_member () const { return member; };
private:
int member = 0;
};
ALLOW_ACCESS(X, member, int);
int main() {
X x;
ACCESS(x, member) = 42;
std::cout << "proof: " << x.get_member() << std::endl;
}
回答by kdgregory
If you know how your C++ compiler mangles names, yes.
如果您知道您的 C++ 编译器如何处理名称,是的。
Unless, I suppose, it's a virtual function. But then, if you know how your C++ compiler builds the VTABLE ...
除非,我想,它是一个虚函数。但是,如果您知道 C++ 编译器如何构建 VTABLE ...
Edit: looking at the other responses, I realize that I misread the question and thought it was about member functions, not member data. However, the point still stands: if you know how your compiler lays out data, then you can access that data.
编辑:查看其他回复,我意识到我误读了这个问题,并认为这是关于成员函数,而不是成员数据。但是,重点仍然存在:如果您知道编译器如何布置数据,那么您就可以访问该数据。