C++中的类为什么必须声明它们的私有函数?这背后是否有实际的技术原因(在编译时起到了什么作用),或者仅仅是出于一致性考虑?
C++中的类为什么必须声明它们的私有函数?这背后是否有实际的技术原因(在编译时起到了什么作用),或者仅仅是出于一致性考虑?
static
。从外部看不到,但对编译器本身很重要。编译器希望在使用函数之前知道其签名。这就是为什么首先要声明函数的原因。请记住,C++ 编译器是一次通过,这意味着必须在使用之前声明所有内容。friend
。被友好的那个类需要知道这个类的私有成员是什么样子的,否则它们无法使用它。private
函数如果是 virtual
的,则可能会影响虚表大小。void *internal;
,但那只是一个解决方法)。 - Shahbazstruct
,被C++采用,所以你无法切片class
(并被其他从C++分支出的语言采用)),我猜这是为了简单性。想象一下,如果你要设计一个计划,让你可以将类分成不同的头文件,让每个人都知道它,并防止其他人向你的类添加东西,那会有多困难。 - Shahbaz有几个问题需要考虑:
因此:
从实际的二进制兼容性角度来看:如David在评论中所说,私有的virtual
函数会影响这个类及使用它作为基类的任何类的vtable的大小和布局。因此,即使编译无法调用它们的代码,编译器仍需了解它们。
C ++可以被发明成另一种方式来允许.cpp文件重新打开类并添加某些特定类型的额外成员函数,并要求实现安排以确保这不会破坏二进制兼容性吗?可以放宽一次定义规则,允许在某些方面不同的定义吗?例如,静态成员函数和非虚非静态成员函数。
可能两者都可以。我认为没有任何技术障碍,尽管当前的ODR非常严格,关于什么使定义“不同”(因此对于允许非常相似的定义之间的二进制不兼容性,实现非常慷慨)。我认为介绍这种例外规则的文本会很复杂。
最终可能取决于“设计者想要那样”,也可能是某人尝试过并遇到了我没有想到的障碍。
private
将不会泄露到模块定义之外,编译器仍然可以知道所有内容。 - Xeo[temp.expl.spec]
“以下任何一个的显式特化:(...)-类或类模板的成员类模板-类或类模板的成员函数模板可以通过声明来声明(...)”“显式特化应在封闭专用模板的命名空间中声明。” - curiousguy访问级别并不影响可见性。私有函数对外部代码可见,并且可能会被重载解析所选中(这将导致访问冲突错误):
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();
}
a.F(1)
编译,如果它仅在A.cpp
中添加,则不会影响客户端代码,但会影响.cpp
文件。这已经是命名空间的工作方式(如果添加更多重载,它们可能会被选中)。需要一些复杂性,例如双相查找会发生什么。 - Steve Jessop= delete
语法显式删除重载。 - balki类的私有成员仍然是类的成员,因此必须声明它们,因为其他公共成员的实现可能依赖于该私有方法。声明它们将允许编译器将对该函数的调用理解为成员函数调用。
如果您有一个仅在 .cpp
文件中使用且不依赖于类的其他私有成员的直接访问的方法,请考虑将其移动到匿名命名空间中。然后,它不需要在头文件中声明。
有几个原因说明为什么必须声明私有函数。
第一个编译时错误检查
访问修饰符的目的是在编译时捕获某些类(没有双关语)的编程错误。私有函数是指,如果有人从类外部调用它们,那将是一个错误,您希望尽早了解这一点。
第二个强制转换和继承
来自C++标准:
3 [注意:私有基类的成员可能作为继承的成员名称不可访问,但可以直接访问。由于指针转换(4.10)和显式转换(5.4)的规则,从派生类的指针到不可访问的基类的指针的转换可能是非法的,如果使用隐式转换,则使用显式转换是合法的。
第三个友元
朋友们互相展示他们的私人部位。另一个类可以通过友元调用私有方法。
第四个普遍的理智和良好的设计
曾经与其他100名开发人员一起工作过吗?拥有标准和一般规则集有助于维护可维护性。对于组内的每个人来说,声明某些内容为私有具有特定的含义。
这也涉及到良好的面向对象设计原则。什么需要暴露,什么不需要。
private
? - GManNickGprivate
和virtual
是正交的概念。函数被声明为private
并不意味着它不是virtual
(事实上,有一个整个习惯用语围绕着仅有private
virtual
和public
非虚拟成员函数),因此,是的,其存在会影响虚表。 - David Rodríguez - dribeas