RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1013941
Accepted
MGNeo
MGNeo
Asked:2020-08-15 22:04:12 +0000 UTC2020-08-15 22:04:12 +0000 UTC 2020-08-15 22:04:12 +0000 UTC

C++ 和循环依赖

  • 772

我想了解是否有任何单一的方法来处理循环依赖?

问题:出于某种原因,项目中的类型之间存在交叉引用。尽管事先对架构进行了规划,但有时仍会发生这种情况。这样的引用并不总是错误的。

这种情况最令人沮丧的是,在大多数情况下,编译器会给你大量的通知。同时,通知指向错误的地方,只有经验有助于或多或少地快速了解原因是循环依赖。

此外,许多人经常忘记它#pragma once可以防止多重包含,而不是防止循环依赖。

例如,有一个场景有一个打架,有一个打架,打架包含一个到父场景的链接:

// BattleScene.hpp
class BattleScene
{
    private:
    Battle battle;
};

// Battle.hpp
class Battle
{
    private:
    BattleScene &parent;
};

处理此类问题的一般方法是什么?

c++ циклические-зависимости
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. Best Answer
    Pavel Mayorov
    2020-08-15T22:54:47Z2020-08-15T22:54:47Z

    为了让编译器不发誓,使用前向声明就足够了:

    class BattleScene;
    class Battle
    {
    private:
        BattleScene &parent;
    };
    

    请注意,已声明但尚未定义的类不能按值使用,只能通过引用或指针使用。

    然而,循环引用还有第二个问题:循环数据结构不能被简单地复制或传输。

    因此,如果您尝试这样做:

    BattleScene copy = someOtherScene;
    

    原来,copy.battle.parent指向的不是to copy,而是to someOtherScene!因此,不要忘记禁止复制和移动!

    class BattleScene;
    class Battle
    {
    private:
        BattleScene &parent;
    
        Battle(const Battle&) = delete;
        Battle(Battle&&) = delete;
        Battle& operator=(const Battle&) = delete;
        Battle& operator=(Battle&&) = delete;
    };
    

    循环结构的另一个问题是可能失去恒定性。因此,如果您有一个变量const BattleScene scene,那么您可以获取- 并获得对!scene.battle.parent的非常量引用scene

    除此之外,额外的链接在记忆中效率很低。

    因此,我建议您考虑另一种方法 - 将父对象作为参数传递给子对象的方法:

    class BattleScene;
    class Battle
    {
    public:
        void Foo(BattleScene& parent);
    };
    
    class BattleScene
    {
    private:
        Battle battle;
    
    public:
        void Bar()
        {
            battle.Foo(*this);
        }
    };
    

    如果您需要在某处单独传递到 Battle 的链接,您可以制作一个包装器:

    class BattleScene;
    class Battle
    {
    public:
        void Foo(BattleScene& parent);
    };
    
    class BattleRef
    {
        BattleScene& scene;
        Battle& battle;
    public:
        BattleRef(BattleScene& scene, Battle& battle) : scene(scene), battle(battle) { }
    
        void Foo()
        {
            battle.Foo(scene);
        }
    };
    
    • 8
  2. Inquisitions
    2020-08-15T22:23:34Z2020-08-15T22:23:34Z
    // BattleScene.hpp
    #include "Battle.hpp"
    
    // Battle.hpp
    class BattleScene;
    
    class Battle
    {
     private:
      BattleScene &parrent;
    };
    
    • 2
  3. AlexGlebe
    2022-04-01T16:07:18Z2022-04-01T16:07:18Z

    可以创建一种处理循环依赖的通用方法,但首先您需要了解声明的顺序以及它们之间的依赖关系。

    第一个简单元素是前向类型声明。然后是类声明及其元素和方法。只是没有在里面声明类方法!这可能会导致编译失败。进一步这些相同的内联函数。然后定义简单的静态方法。

    假设有两个类包含对彼此的引用以及具有另一个类的类型参数的方法。这是一个如何正确声明所有内容的示例:

    A.hpp:

    # pragma once
    // предварительное объявление класса B
    class B ;
    
    class A {
    public :
      A ( B & ) ;
      B & theB();
      // аргументом по значению тоже можно
      void  Argument_B_by_value ( B );
    private :
      B & b ;
    } ;
    

    b.hpp:

    # pragma once
    // предварительное объявление класса A
    class A ;
    
    class B {
    public :
      B ( A & ) ;
      A & theA();
      // аргументом по значению тоже можно
      void  Argument_A_by_value ( A );
    private :
      A & a ;
    } ;
    

    爱.hpp:

    // порядок не важен
    # include "A.hpp"
    # include "B.hpp"
    
    inline B & A :: theB ( ) {
      return b ; }
    
    inline void  A :: Argument_B_by_value ( B ) {
      }
    

    比.hpp:

    // порядок не важен
    # include "B.hpp"
    # include "A.hpp"
    
    inline A & B :: theA ( ) {
      return a ; }
    
    inline void  B :: Argument_A_by_value ( A ) {
      }
    

    A.cpp:

    // порядок не важен
    # include "A.hpp"
    # include "B.hpp"
    // порядок не важен
    # include "Ai.hpp"
    # include "Bi.hpp"
    
    A :: A ( B & x ) : b { x } { }
    

    b.cpp:

    // порядок не важен
    # include "B.hpp"
    # include "A.hpp"
    // порядок не важен
    # include "Bi.hpp"
    # include "Ai.hpp"
    
    B :: B ( A & x ) : a { x } { }
    

    结果是具有不同文件名的三个级别:

    1. 其他类型的前向声明。类定义。

    2. 内联方法。

    3. 静态方法。

    第二级包括第一级的标题。第三级包括第一级和第二级的标题。

    • 1

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    根据浏览器窗口的大小调整背景图案的大小

    • 2 个回答
  • Marko Smith

    理解for循环的执行逻辑

    • 1 个回答
  • Marko Smith

    复制动态数组时出错(C++)

    • 1 个回答
  • Marko Smith

    Or and If,elif,else 构造[重复]

    • 1 个回答
  • Marko Smith

    如何构建支持 x64 的 APK

    • 1 个回答
  • Marko Smith

    如何使按钮的输入宽度?

    • 2 个回答
  • Marko Smith

    如何显示对象变量的名称?

    • 3 个回答
  • Marko Smith

    如何循环一个函数?

    • 1 个回答
  • Marko Smith

    LOWORD 宏有什么作用?

    • 2 个回答
  • Marko Smith

    从字符串的开头删除直到并包括一个字符

    • 2 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5