为什么C ++中的类必须声明它们的私有函数?它有实际的技术原因(它在编译时的作用是什么)还是仅仅是出于一致性的原因?
答案 0 :(得分:21)
我问为什么必须要声明私人功能,因为他们不会添加任何内容(对象大小和vtable条目)以供其他翻译单位知道
如果你考虑一下,这类似于在文件中声明一些函数static
。它从外部看不到,但它对编译器本身很重要。编译器想要知道函数的签名才能使用它。这就是你首先申报功能的原因。 请记住,C ++编译器是一次传递,这意味着在使用之前必须声明所有内容。 1
从程序员的角度来看,声明私有函数仍然没有完全无用。想象一下2个类,其中一个是另一个类的friend
。友好的班级 2 需要知道该班级的私有化是什么样的,(这个讨论变得很奇怪),否则他们就无法使用它。
至于为什么C ++正是以这种方式设计的,我首先要说的是历史原因:你不能用C切片结构的事实被C ++采用,所以你不能这样做切片一个类(并且也被从C ++分支的其他语言采用)。我还猜测它是关于简单性的:想象一下设计一种编译方法是多么困难,你可以在不同的头文件之间拆分类,让你的源文件知道它,并防止其他人在你班上添加东西。
最后需要注意的是,private
函数可以影响vtable大小。也就是说,如果它们是virtual
。
1 实际上并非完全如此。如果类中有内联函数,则它们可以引用稍后在同一类中定义的函数。但可能这个想法是从单一传递开始的,后来又加入了这个例外。
2 特别是内联成员函数。
答案 1 :(得分:20)
您必须在类本身的定义中声明所有成员,以便编译器知道哪些函数允许成为成员。否则,第二个程序员可能(意外地?)出现并添加成员,犯错并违反对象的保证,导致未定义的行为和/或随机崩溃。
答案 2 :(得分:3)
有一些问题,但是:
因此:
来自实际二进制兼容性的POV:正如David在评论中所说,私有virtual
函数会影响此类的vtable的大小和布局以及使用它作为基础的任何类。因此编译器即使在编译无法调用它们的代码时也需要了解它们。
C ++可能是以不同的方式发明的,以允许.cpp文件重新打开类并添加某些类型的附加成员函数,并且需要实现以确保这不会破坏二进制兼容性吗?可以放宽一个定义规则,以允许在某些方面有所不同的定义吗?例如,静态成员函数和非虚拟非静态成员函数。
对两者都可能是肯定的。我不认为存在任何技术障碍,尽管当前的ODR对于使定义“不同”的内容严格非常严格(因此对于允许非常相似的定义之间的二进制不兼容性的实现非常慷慨)。我认为在规则中引入这种例外的文本会很复杂。
最终它可能归结为“设计师想要这样”,或者可能是有人试过它并遇到了一个我没有想过的障碍。
答案 3 :(得分:2)
访问级别不会影响可见性。私有函数对外部代码是可见的,可以通过重载解析来选择(这会导致访问violoation错误):
class A {
void F(int i) {}
public:
void F(unsigned i) {}
};
int main() {
A a;
a.F(1); // error, void A::F(int) is private
}
想象一下这种情况下的混乱:
class A {
public:
void F(unsigned i) {}
};
int main() {
A a;
a.F(1);
}
// add private F overload to A
void A::F(int i) {}
但是将其更改为第一个代码会导致重载决策选择不同的函数。以下示例又如何呢?
class A {
public:
void F(unsigned i) {}
};
// add private F overload to A
void A::F(int i) {}
int main() {
A a;
a.F(1);
}
或者这是另一个出错的例子:
// A.h
class A {
public:
void g() { f(1); }
void f(unsigned);
};
// A_private_interface.h
class A;
void A::f(int);
// A.cpp
#include "A_private_interface.h"
#include "A.h"
void A::f(int) {}
void A::f(unsigned) {}
// main.cpp
#include "A.h"
int main() {
A().g();
}
答案 4 :(得分:1)
一个原因是在C ++中,朋友可以访问您的私有。要让朋友访问它们,朋友必须了解它们。
答案 5 :(得分:0)
类的私有成员仍然是类的成员,因此必须声明它们,因为其他公共成员的实现可能依赖于该私有方法。声明它们将允许编译器将对该函数的调用理解为成员函数调用。
如果您的方法仅在.cpp
文件中使用,并且不依赖于对该类的其他私有成员的直接访问,请考虑将其移动到匿名命名空间。然后,它不需要在头文件中声明。
答案 6 :(得分:0)
为什么必须声明私有函数有几个原因。
首次编译时错误检查
访问修饰符的意思是在编译时捕获编程错误的某些类(没有双关语)。私有函数是一些函数,如果有人从类外部调用它们,那将是一个错误,并且您希望尽早了解它。
第二次投射和继承
取自C ++标准:
3 [注意:私有基类的成员可能无法作为继承的成员名称访问,但可以直接访问。由于指针转换(4.10)和显式转换(5.4)的规则,如果使用隐式转换,但是从指向派生类的指针转换为指向不可访问的基类的指针可能是不正确的如果使用了显式强制转换。
第三个朋友
朋友们互相表示私下。私有方法可以由另一个朋友的类调用。
第四届全面理智和优秀设计
曾与另外100名开发人员合作过一个项目。拥有标准规则和一般规则有助于保持可维护性。声明私有的东西对小组中的其他人都有特定的意义。
此外,这也符合良好的OO设计原则。揭露什么,不是什么