简体   繁体   English

C ++:为什么必须声明私有函数?

[英]C++: Why must private functions be declared?

Why do classes in C++ have to declare their private functions? 为什么C ++中的类必须声明其私有函数? Has it actual technical reasons (what is its role at compile time) or is it simply for consistency's sake? 是实际的技术原因(在编译时它的作用是什么)还是仅仅是出于一致性考虑?

I asked why private functions had to be declared at all, as they don't add anything (neither object size nor vtable entry) for other translation units to know 我问为什么为什么必须要声明私有函数,因为它们没有为其他翻译单元添加任何内容(对象大小或vtable条目)

If you think about it, this is similar to declaring some functions static in a file. 如果您考虑一下,这类似于在文件中声明某些static函数。 It's not visible from the outside, but it is important for the compiler itself. 从外部看不到它,但是对编译器本身很重要。 The compiler wants to know the signature of the function before it can use it. 编译器希望在使用该功能之前先知道该功能的签名。 That's why you declare functions in the first place. 这就是为什么首先声明函数的原因。 Remember that C++ compilers are one pass, which means everything has to be declared before it is used. 请记住,C ++编译器是一回事,这意味着在使用之前必须先声明所有内容。 1 1个

From the programmer's point of view, declaring private functions is still not completely useless. 从程序员的角度来看,声明私有函数仍然不是完全没有用的。 Imagine 2 classes, one of which is the friend of the other. 想象一下2个类,其中一个是另一个的friend The friendzoned class 2 would need to know how the privates of that class look like, (This discussion is getting weird) otherwise they can't use it. 带有朋友圈的第2类将需要知道该类的私有对象的外观(此讨论变得很奇怪),否则他们将无法使用它。

As to why exactly C++ was designed in this way, I would first say there is the historical reason: the fact that you can't slice a struct in C, was adopted by C++ so you can't slice a class (and adopted by other languages branched from C++, too). 关于为什么精确地以这种方式设计C ++,我首先要说有一个历史原因:C ++采用了无法在C中切片结构这一事实,因此您无法切片类(并被其他语言也来自C ++)。 I'd also guess that it's about simplicity: Imagine how difficult it would be to devise a method of compilation in which you can split the class among different header files, let your source files know about it, and prevent others from adding stuff to your class. 我还猜想这是关于简单性的:想象一下设计一种编译方法将有多么困难,在这种方法中,您可以将类拆分到不同的头文件中,让您的源文件知道它,并阻止其他人向您添加内容类。

A final note is that, private functions can affect vtable size. 最后一点是, private函数影响vtable的大小。 That is, if they are virtual . 也就是说,如果它们是virtual


1 Actually not entirely. 1 实际上并非完全如此。 If you have inline functions in the class, they can refer to functions later defined in the same class. 如果您在类中具有内联函数,则它们可以引用稍后在同一类中定义的函数。 But probably the idea started from single pass and this exception later added to it. 但是可能这个想法是从单次通过开始的,后来又增加了这种例外。

2 It's inlined member functions in particular. 2 特别是内联的成员函数。

You have to declare all members in the definition of the class itself so that the compiler knows which functions are allowed to be members. 您必须在类本身的定义中声明所有成员,以便编译器知道允许哪些函数成为成员。 Otherwise, a second programmer could (accidentally?) come along and add members, make mistakes, and violate your object's guarantees, causing undefined behavior and/or random crashes. 否则,第二个程序员可能会(偶然地?)出现并添加成员,犯错误并违反对象的保证,从而导致不确定的行为和/或随机崩溃。

There's a combination of concerns, but: 有很多顾虑,但是:

  • C++ doesn't let you re-open a class to declare new members in it after its initial definition. C ++不允许您在初始定义后重新打开类以在其中声明新成员。
  • C++ doesn't let you have different definitions of a class in different translation units that combine to form a program. C ++不允许您将不同的翻译单元结合在一起以形成一个程序,从而拥有不同的类定义。

Therefore: 因此:

  • Any private member functions that the .cpp file wants declared in the class need to be defined in the .h file, which every user of the class sees too. .cpp文件要在类中声明的所有私有成员函数都需要在.h文件中定义,该类的每个用户也可以看到。

From the POV of practical binary compatibility: as David says in a comment, private virtual functions affect the size and layout of the vtable of this class and any classes that use it as a base. 从实用的二进制兼容性的POV中看:正如David在评论中所说,私有virtual函数会影响此类以及将其用作基础的任何类的vtable的大小和布局。 So the compiler needs to know about them even when compiling code that can't call them. 因此,即使在编译无法调用它们的代码时,编译器也需要了解它们。

Could C++ have been invented differently, to allow the .cpp file to reopen the class and add certain kinds of additional member functions, with the implementation required to arrange that this doesn't break binary compatibility? 是否可以以不同的方式发明C ++,以允许.cpp文件重新打开类并添加某些种类的其他成员函数,并且需要实现不破坏二进制兼容性的实现? Could the one definition rule be relaxed, to allow definitions that differ in certain ways? 是否可以放宽一个定义规则,以允许在某些方面有所不同的定义? For example, static member functions and non-virtual non-static member functions. 例如,静态成员函数和非虚拟非静态成员函数。

Probably yes to both. 可能两者都是。 I don't think there's any technical obstacle, although the current ODR is very strict about what makes a definition "different" (and hence is very generous to implementations in allowing binary incompatibilities between very similar-looking definitions). 我认为没有任何技术障碍,尽管当前的ODR对于使定义“与众不同”的内容非常严格(因此,对于允许看起来非常相似的定义之间的二进制不兼容的实现,它是非常慷慨的)。 I think the text to introduce this kind of exception to the rule would be complex. 我认为将这种例外引入规则的案文会很复杂。

Ultimately it might come down to, "the designers wanted it that way", or it might be that someone tried it and encountered an obstacle that I haven't thought of. 最终,它可能归结为“设计师以这种方式想要它”,或者可能是有人尝试了它,遇到了我没有想到的障碍。

The access level does not affect visibility. 访问级别不影响可见性。 Private functions are visible to external code and may be selected by overload resolution (which would then result in an access violoation error): 私有函数对于外部代码是可见的,并且可以通过重载解决方案进行选择(这将导致访问冲突错误):

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
}

Imagine the confusion when this works: 想象一下,当这种方法起作用时的困惑:

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) {}

But changing it to the first code causes overload resolution to select a different function. 但是将其更改为第一个代码会导致重载分辨率以选择其他函数。 And what about the following example? 那下面的例子呢?

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);
}

Or here's another example of this going wrong: 或者这是另一个出错的例子:

// 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();
}

One reason is that in C++ friends can access your privates. 原因之一是,在C ++中,朋友可以访问您的私有对象。 For friends to access them, friends have to know about them. 为了让朋友访问它们,朋友必须了解它们。

Private members of a class are still members of the class, so they must be declared, as the implementation of other public members might depend on that private method. 类的私有成员仍然是该类的成员,因此必须声明它们,因为其他公共成员的实现可能依赖于该私有方法。 Declaring them will allow the compiler to understand a call to that function as a member function call. 声明它们将使编译器将对该函数的调用理解为成员函数调用。

If you have a method that only is used int the .cpp file and does not depend on direct access to other private members of the class, consider moving it to an anonymous namespace. 如果您有一个仅在.cpp文件中使用的方法,并且不依赖于对类的其他私有成员的直接访问,请考虑将其移至匿名名称空间。 Then, it does not need to be declared in the header file. 然后,不需要在头文件中声明它。

There are a couple of reason on why private functions must be declared. 为什么必须声明私有函数有两个原因。

First Compile Time Error Checks 首次编译时错误检查

the point of access modifiers is to catch certain classes (no pun intended) of programming errors at compile time. 访问修饰符的目的是在编译时捕获某些类的编程错误(无双关语)。 Private functions are functions that, if someone called them from outside the class, that would be a bug, and you want to know about it as early as possible. 私有函数是这样的函数,如果有人从类外部调用它们,那将是一个错误,并且您希望尽早了解它。

Second Casting and Inheritance 第二次铸造和继承

Taken from the C++ standard: 取自C ++标准:

3 [ Note: A member of a private base class might be inaccessible as an inherited member name, but accessible directly. 3 [注意:私有基类的成员可能无法作为继承的成员名称访问,但可以直接访问。 Because of the rules on pointer conversions (4.10) and explicit casts (5.4), a conversion from a pointer to a derived class to a pointer to an inaccessible base class might be ill-formed if an implicit conversion is used, but well-formed if an explicit cast is used. 由于有关于指针转换(4.10)和显式转换(5.4)的规则,如果使用隐式转换,则从指向派生类的指针到指向不可访问基类的指针的转换可能格式不正确,但格式正确如果使用显式强制转换。

3rd Friends 第三朋友

Friends show each other there privates. 朋友们在那里互相展示私密性。 A private method can be call by another class that is a friend. 私有方法可以由另一个作为朋友的类调用。

4th General Sanity and Good Design 第四届理智与良好设计

Ever worked on a project with another 100 developers. 曾经与另外100个开发人员一起从事过一个项目。 Having a standard and a general set of rule helps maintain maintainable. 拥有一套标准和一套通用的规则有助于保持可维护性。 declaring something private has a specific meaning to everyone else in the group. 声明私有内容对小组中的其他每个人都有特定的含义。

Also this flows into good OO design principles. 同样,这也成为良好的OO设计原则。 What to expose and what not 暴露什么,不暴露什么

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM