【问题标题】:C++ and incomplete friend classesC++ 和不完整的友元类
【发布时间】:2021-01-27 14:50:32
【问题描述】:

我环顾四周,寻找在 C++ 编译器中选择我所谓的虚构朋友的理由。不幸的是,我没有找到任何解释,所以我请教专家。

我想知道为什么 C++ 不抱怨以下代码。类和方法作为朋友表示实际上并不存在,但是如果你编译程序它就会编译并运行。

#include <iostream>

class TrueFriend
{
    friend class NotExistingClass;
    friend class AnotherNotExistingClass;
    friend int NotExistingFunc();
  public:
     void TellAboutYou() { std::cout << "I have a lot of friends" << std::endl; }
};

int main(int argc, char **argv)
{
   TrueFriend instance;
   instance.TellAboutYou();
   return 0;
}

【问题讨论】:

  • 转发声明一个类型然后不定义它,如果你不实际使用该类型,不会导致我使用的编译器出错。
  • 我觉得关键字friend 在这里并不重要,没有它你也可以这样做godbolt link
  • 一种思考方式是回想 C++ 一次编译一个翻译单元,而不知道其他翻译单元的内容。 friend class NotExistingClass; 是一个前向声明,基本上说“当你开始编译 NotExistingClass 时,它是 TrueFriendfriend。”但是当您在一个文件中编译TrueFriend 时,它应该如何知道您是在其他文件中最终 还是从不 编译NotExistingClass?答案是它不知道也不必关心,所以它不关心。
  • 这些声明与其说是“这个存在,而且是我的朋友”,不如说是“如果这个存在,那就是我的朋友”。 (顺便说一句,声明一个你从未定义过的类型非常有用。)
  • @NathanPierson 是正确的,但我的示例是一个可执行文件,因此它也将通过链接阶段。这对我来说似乎很奇怪,而且碰巧这样的代码只是混杂着无用的信息,使得代码难以阅读。

标签: c++ keyword friend


【解决方案1】:

没有理由使此代码无法编译。朋友声明足以声明类型。在您的代码中不需要定义。也许这会更清楚:

class NotExistingClass;
class AnotherNotExistingClass;
int NotExistingFunc();

class TrueFriend
{
     friend NotExistingClass;
     friend AnotherNotExistingClass;
     friend int NotExistingFunc();
   public:
     void TellAboutYou() { std::cout << "I have a lot of friends" << std::endl; }
};

同样的效果:声明了类和函数,但没有可用的定义。在这里您可以了解前向声明的优点以及为什么有时需要它们:What are forward declarations in C++?

例如考虑两种类型的AB,它们是共同的朋友。那是您需要前向声明的情况,因为标头不能相互包含:

// A.h
class B;
class A{ friend B;};

// B.h
#include "A.h"
class B{ friend A;};

A.h的更短的方式是

class A{ friend class B; };

现在考虑您开始一个新项目,该项目仅使用 A 而不是 B。你包括A.h,永远不要使用B,一切都很好。

PS:正如评论中提到的,friend 并不是您问题的真正本质。声明类型和函数但未定义的情况并不少见,有时是无法避免的。

【讨论】:

    【解决方案2】:

    我想知道为什么 C++ 不抱怨下面的代码。

    C++ 是一种编程语言规范。

    您可以阅读一些 C++ 标准,例如 n3337(或更新的标准)。该规范没有提到“投诉”。

    GCC 是一个或多或少实现 C++ 的编译器。您可以添加自己的 GCC plugin 以发出您想要的诊断信息。

    这样的 GCC 插件会减慢编译速度。

    您可以尝试使用Clang static analyzer。由于它是开源的,因此您可以对其进行改进。

    顺便说一句,当需要不存在的朋友类时,这是一个很好的例子。

    您正在设计一个GUI 库(如FOX)。在某些情况下,某些小部件没有实现(例如,被一些 #ifdef HAVE_FOO_FEATURE 删除),但它们的类需要成为某个现有类的朋友。以一些与声音相关的小部件为例:在没有扬声器的 PC 上,它没有任何意义。或者想想硬件上的一些OpenGL 相关小部件 不支持 OpenGL。或者黑白屏幕上的一些彩色小部件....

    或者你的 C++ 代码已经被GNU autoconf 配置了。在某些情况下,某些类会被禁用。

    或者您正在编写一个库,并与您的团队集体决定将一个未来 C++ 类命名为NotExistingClass 和您的同事 John 负责实现是在节假日,分配给更紧急的工作,或者生病了。

    同样,在一些class WorkInProgessClass { ... }声明void some_missing_member_function() 并在几周后真正需要它时实现WorkInProgessClass::some_missing_member_function 是合理的。 p>

    public APIs 应该比实现它们的代码更稳定。

    基本原理是,在团队开发的项目中,您希望(在文档和stable 头文件中)指定公共API,并在以后实施。几个月后,您会发现根本不需要某些已定义并记录在案的函数。

    一旦您团队中的 Isabel(开发人员)使用WorkInProgessClass::some_missing_member_function,她将立即实施它(否则,linker 会抱怨)。同样,如果她声明一个转发的class NotExistingClass 的对象,链接器会发出一些错误(可能:调用/使用未定义的NotExistingClass::NotExistingClass() 构造函数...)

    我喜欢使用g++ -Wall -Wextra -gGCC 编译C++ 项目(如RefPerSys),如果收到您梦寐以求的警告,我会很生气。

    另一个非常相关的例子是缺少virtual 方法(在某些抽象超类中声明为virtual void foo(void) =0;),具体子类在运行时从插件加载(在Linux 上,使用dlopen(3)...)。 AFAIK、QtFLTK 正在这样做。

    【讨论】:

    • 但是如何回答这个问题呢?告诉人们只阅读标准(并停止提问?)并没有真正的帮助。
    • C++ 不能抱怨,编译器可以。这在技术上是正确的,最好的正确性
    • 是一个不完整的类型,即使是 C++ 错误或未定义的行为也从未完成?我找不到答案
    • 这是一个在您编辑后变得更好的答案,我怀疑最初的反对者没有看到更改。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-19
    • 2018-05-15
    • 2016-10-06
    • 2012-04-15
    • 1970-01-01
    • 2013-09-15
    • 2012-11-18
    相关资源
    最近更新 更多