我在C ++中尝试private
访问说明符的有效性。这是:
接口:
// class_A.h
class A
{
public:
void printX();
private:
void actualPrintX();
int x;
};
实现:
// class_A.cpp
void A::printX()
{
actualPrintX();
}
void A::actualPrintX()
{
std::cout << x:
}
我将其构建到静态库(.a / .lib)中。我们现在有一个class_A.h和classA.a(或classA.lib)对。 我编辑了class_A.h并从中删除了private:
。
现在在另一个classTester.cpp中:
#include "class_A.h" // the newly edited header
int main()
{
A a;
a.x = 12; // both G++ and VC++ allowed this!
a.printX(); // allowed, as expected
a.actualPrintX(); // allowed by G++, VC++ gave a unresolved linker error
return 0;
}
我知道在篡改了库的标题后,所有的赌注都关闭了(我的意思是,系统完整性等等)虽然方法很黑,但这真的允许吗?有办法阻止这个吗?或者我在这里做错了什么?
答案 0 :(得分:30)
private
不是一种安全机制。这是一种沟通意图和隐藏信息的方式,程序的其他部分不需要知道,从而降低整体复杂性。
拥有两个不同的头文件并不符合标准,因此从技术上讲,您将进入未定义的行为区域,但实际上,正如您所发现的,大多数编译器都不会关心。
答案 1 :(得分:9)
你已经偏离了C ++允许的范围,所以你所做的事情是不被允许的 - 当然在某些情况下它可能适用于某些编译器。
具体而言,您违反了One Definition Rule。
Herb Sutter的article很好地解释了它 - 它还提供了一种绕过访问说明符系统的合法且可移植的方法。
答案 2 :(得分:7)
没有。私有访问控制可以阻止您做愚蠢的事情,而不是作为阻止其他人访问您的数据或功能的安全机制。有许多方法可以绕过它。
答案 3 :(得分:5)
我试过了。这是我写的一个程序的nm,它有一个使用一个私有方法和一个公共方法的类Test。
0000000100000dcc T __ZN4Test3barEv
0000000100000daa T __ZN4Test3fooEv
如您所见,签名完全相同。链接器完全没有区分私有方法和公共方法。
答案 4 :(得分:5)
我同意大多数其他答案。
但是,我想指出,当您删除private
时,编译器以不同方式对成员进行物理排列是完全可以接受的。如果它有效,那就是运气。你不能指望它。如果双方都没有使用相同的声明,那么他们并没有真正使用同一个类。
答案 5 :(得分:2)
任何地方都没有A :: actualPrintX实现。那是你的链接器错误。
答案 6 :(得分:2)
由于没有人提到阻止这种方法......阻止访问私有成员的一种可能方法是将它们声明为在文件外部不可见的单独内部类型。当然,如果要提供对此内部的显式访问权限,则必须提供内部声明。这通常也是使用包含该类型的内部头文件完成的。
注意:在此示例中,您必须跟踪分配/释放该内部对象。还有其他方法可以做到这一点。
// class_A.h
class A {
public:
void printX();
private:
void *Private;
};
// class_A.cpp
class A_private {
void actualPrintX();
int x;
};
void A::printX() {
reinterpret_cast<A_private *>(Private)->actualPrintX();
}
void A_private::actualPrintX() {
std::cout << x:
}
答案 7 :(得分:1)
在大多数情况下,您甚至不必编辑头文件以使私有成员公开。您可以使用预处理器执行此操作。像这样:
//"classWithPrivateMembers.hpp"
class C
{
private: //this is crucial for this method to work
static int m;
};
int C::m = 12;
然后,这将有效:
#define private public
#include "classWithPrivateMembers.hpp"
#undef private
int main()
{
C::m = 34; // it works!
}
答案 8 :(得分:1)
还要记住,当您更改成员变量的访问权限时,编译器可能会将其放在类对象中的不同偏移处。该标准允许编制者在重新安排成员时有相当大的自由度(至少在同一访问级别内,我认为)。