简体   繁体   中英

C++: Why must private functions be declared?

Why do classes in C++ have to declare their private functions? 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

If you think about it, this is similar to declaring some functions static in a file. 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. 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. 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.

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). 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. That is, if they are virtual .


1 Actually not entirely. 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.

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++ doesn't let you have different definitions of a class in different translation units that combine to form a program.

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.

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. 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? 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). 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. 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. 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:

3 [ Note: A member of a private base class might be inaccessible as an inherited member name, but accessible directly. 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.

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. 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. What to expose and what not

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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