繁体   English   中英

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

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

为什么C ++中的类必须声明其私有函数? 是实际的技术原因(在编译时它的作用是什么)还是仅仅是出于一致性考虑?

我问为什么为什么必须要声明私有函数,因为它们没有为其他翻译单元添加任何内容(对象大小或vtable条目)

如果您考虑一下,这类似于在文件中声明某些static函数。 从外部看不到它,但是对编译器本身很重要。 编译器希望在使用该功能之前先知道该功能的签名。 这就是为什么首先声明函数的原因。 请记住,C ++编译器是一回事,这意味着在使用之前必须先声明所有内容。 1个

从程序员的角度来看,声明私有函数仍然不是完全没有用的。 想象一下2个类,其中一个是另一个的friend 带有朋友圈的第2类将需要知道该类的私有对象的外观(此讨论变得很奇怪),否则他们将无法使用它。

关于为什么精确地以这种方式设计C ++,我首先要说有一个历史原因:C ++采用了无法在C中切片结构这一事实,因此您无法切片类(并被其他语言也来自C ++)。 我还猜想这是关于简单性的:想象一下设计一种编译方法将有多么困难,在这种方法中,您可以将类拆分到不同的头文件中,让您的源文件知道它,并阻止其他人向您添加内容类。

最后一点是, private函数影响vtable的大小。 也就是说,如果它们是virtual


1 实际上并非完全如此。 如果您在类中具有内联函数,则它们可以引用稍后在同一类中定义的函数。 但是可能这个想法是从单次通过开始的,后来又增加了这种例外。

2 特别是内联的成员函数。

您必须在类本身的定义中声明所有成员,以便编译器知道允许哪些函数成为成员。 否则,第二个程序员可能会(偶然地?)出现并添加成员,犯错误并违反对象的保证,从而导致不确定的行为和/或随机崩溃。

有很多顾虑,但是:

  • C ++不允许您在初始定义后重新打开类以在其中声明新成员。
  • C ++不允许您将不同的翻译单元结合在一起以形成一个程序,从而拥有不同的类定义。

因此:

  • .cpp文件要在类中声明的所有私有成员函数都需要在.h文件中定义,该类的每个用户也可以看到。

从实用的二进制兼容性的POV中看:正如David在评论中所说,私有virtual函数会影响此类以及将其用作基础的任何类的vtable的大小和布局。 因此,即使在编译无法调用它们的代码时,编译器也需要了解它们。

是否可以以不同的方式发明C ++,以允许.cpp文件重新打开类并添加某些种类的其他成员函数,并且需要实现不破坏二进制兼容性的实现? 是否可以放宽一个定义规则,以允许在某些方面有所不同的定义? 例如,静态成员函数和非虚拟非静态成员函数。

可能两者都是。 我认为没有任何技术障碍,尽管当前的ODR对于使定义“与众不同”的内容非常严格(因此,对于允许看起来非常相似的定义之间的二进制不兼容的实现,它是非常慷慨的)。 我认为将这种例外引入规则的案文会很复杂。

最终,它可能归结为“设计师以这种方式想要它”,或者可能是有人尝试了它,遇到了我没有想到的障碍。

访问级别不影响可见性。 私有函数对于外部代码是可见的,并且可以通过重载解决方案进行选择(这将导致访问冲突错误):

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

原因之一是,在C ++中,朋友可以访问您的私有对象。 为了让朋友访问它们,朋友必须了解它们。

类的私有成员仍然是该类的成员,因此必须声明它们,因为其他公共成员的实现可能依赖于该私有方法。 声明它们将使编译器将对该函数的调用理解为成员函数调用。

如果您有一个仅在.cpp文件中使用的方法,并且不依赖于对类的其他私有成员的直接访问,请考虑将其移至匿名名称空间。 然后,不需要在头文件中声明它。

为什么必须声明私有函数有两个原因。

首次编译时错误检查

访问修饰符的目的是在编译时捕获某些类的编程错误(无双关语)。 私有函数是这样的函数,如果有人从类外部调用它们,那将是一个错误,并且您希望尽早了解它。

第二次铸造和继承

取自C ++标准:

3 [注意:私有基类的成员可能无法作为继承的成员名称访问,但可以直接访问。 由于有关于指针转换(4.10)和显式转换(5.4)的规则,如果使用隐式转换,则从指向派生类的指针到指向不可访问基类的指针的转换可能格式不正确,但格式正确如果使用显式强制转换。

第三朋友

朋友们在那里互相展示私密性。 私有方法可以由另一个作为朋友的类调用。

第四届理智与良好设计

曾经与另外100个开发人员一起从事过一个项目。 拥有一套标准和一套通用的规则有助于保持可维护性。 声明私有内容对小组中的其他每个人都有特定的含义。

同样,这也成为良好的OO设计原则。 暴露什么,不暴露什么

暂无
暂无

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

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