可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
This question has already been asked here -- more than twice, actually --, but I myself haven't been able to derive a solution to my problem from the posts.
What I have is a library, with, among others, a class named A
in it. From class A
I need to access an std::map<>
, but it is private. Also, and based on the possibilities I found in the post I mentioned, class A
has no templated function.
I'm actually able to recompile the library, so that I could simply change the visibility. However, that would be a lot of work -- and I'm not sure if changing the visibility won't get anything else to crash.
What I'd like to do is, in a class B
:
// NOT MY CODE -- library <a.h>
class A {
private:
std::map<int, int> A_map;
};
// MY CODE -- module "b.h"
# include <a.h>
class B : private A {
public:
B() {
for (auto it(A_map.begin()); it != A_map.end(); ++it) {
...;
}
}
};
How may I do it without changing the original class -- and without having any available templated method in the base class for to be overloaded/specialized?
回答1:
You should first be very confident that what you're trying to do is actually valid... there's probably a good reason that variable is private. Modifying it may break the state of an instance of A, and there's no guarantee a private variable will be in a logical or consistent state when you break encapsulation to read it.
With that caveat, if you can modify / recompile the library declaring your class a friend of A is probably the way to go. Have B hold an instance of A by composition, and since it's a friend it can access private members of the A instance.
And, because I'm feeling particularly evil here's a demo of how to break encapsulation without even modifying A. Although it's not portable (I'm working in g++ on 64-bit linux) and involves figuring out the in-memory object layout. I changed the map to a vector for simplicity.
#include<vector>
#include<iostream>
class A {
private:
int blah; //Just to make it a bit more realistic.
std::vector<int> A_vec;
public:
void outputVec() {
std::vector<int>::iterator it = A_vec.begin();
while(it != A_vec.end()) {
std::cout << *it << std::endl;
++it;
}
}
};
int main() {
A* a = new A();
std::vector<int>* v = (std::vector<int>*)(((char*)a)+sizeof(long));
v->push_back(27);
v->push_back(12);
a->outputVec();
return 0;
}
回答2:
Private variables are deliberately protected from any outside access. You can only access members if the owning class permits it via less restrictive access or making friends.
and I'm not sure if changing the visibility won't get anything else to crash.
Access restrictions in C++ are deliberately designed so that accessibility is the last thing checked when doing name lookups. That means if you have a working program and you make something more accessible then the program's behavior shouldn't change at all, because all other potential problems were already checked before checking accessibility.
回答3:
First of all, it is a very good idea to keep all data members private. If your derived class really needs access to A_map
, then the best thing to do is to give the base class a getter that returns a const reference to it (assuming you only need read-access). If you need write access to A_map
, then that is a good indication that you need to rethink your design.
Other than that, there is no way to grant class B
access to A_map
without seriously bending the rules and conventions of C++.
By the way, private inheritance means something else. It means that all public methods of class A
would become private methods of class B
. In other words, private inheritance means that you inherit the implementation of A
, but not its interface. So it will not help you here.
回答4:
Private inheritance is really a composition in disguise! It actually means "implemented in terms of".
Putting that aside, irrespective of private/public/protected mode of inheritance, private members would remain private(not accessible outside of that class, even from the derived ones).
There are two legitimate ways to access private members of any class.
1) Access private members using Get/Set methods(assuming the class exposes it).
2) Befriend with the class.
In your case, class A belongs to a library, it is the responsibility of library vendors to provide proper interface for the external world to interact with class A. Perhaps, it is better to have a talk with the library vendor. To me, it looks more like a design issue rather than implementation issue.
Just for testing, as a quick hack, I would suggest befriending mechanism.
class A {
friend class B;
private:
std::map<int, int> A_map;
};
// MY CODE -- module "b.h"
class B {
public:
B() {
for (std::map<int, int>::iterator it(a.A_map.begin());
it != a.A_map.end(); ++it) { }
}
A a;
};