【问题标题】:Alternatives to passing a pointer to yourself to objects you own将指向您自己的指针传递给您拥有的对象的替代方法
【发布时间】:2013-04-10 06:34:44
【问题描述】:

我经常这样做:

class Child{
  Control*parent;
public:
  Child(Control*theParent):parent(theParent){}
 };

class Control{
 Child child;
 void makeChild(){ child=Child(this); }
 //Control() : child(this) { }  //another example
 }

所以控件拥有并操作子控件,但是子控件仍然有对父控件的引用。

当我阅读有关程序设计模式等的一般主题时,似乎并不特别推荐这种组织对象的方法。我意识到存在风险,例如,您不希望您的 child 通过让其父母做只有父母应该自己决定做的事情来滥用其父母;在这种情况下,通过确保父母的关键方法是私有的,似乎很容易防止。但是,您可能有另一个控制器来管理父级,在这种情况下,某些方法应该是公共的,而现在您的孩子也可以访问这些方法,而实际上只有其他一些控制器才能操作这些方法。

所以我知道这有多危险。

但我的问题是,什么是常见的替代方案?您需要父母拥有孩子,但您需要孩子能够偶尔通知父母。 如果不是上述情况,怎么办?

Objective-C 有 NSNotificationCenter,它允许通知而不实际传递引用,但这也很容易造成组织问题(为什么我的孩子会发出这个通知,谁会收到它?)

【问题讨论】:

  • 很好的问题,我经常这样做,我想知道(并且仍然想知道)是否有替代方案。
  • 应该更像Control() : child(this) { },不是吗?
  • @KerrekSB 是的,我没有编译就输入了伪代码;我已经改正了

标签: c++ design-patterns reference


【解决方案1】:

您可以通过界面将它们分开:

class IListener {
public:
    virtual void notify (int somevalue) = 0;
    ~IListener() {};
}

class Child{
private:
    IListener *listener;
public:
    Child(IListener *theListener):listener(theListener){}
};

class Control: public IListener {
private:
    Child child;
public:
    makeChild(){ child(this) };
    virtual void notify(int someValue) { doSomething(someValue);};
}

(这是观察者模式的简单版本,顺便说一句)。

【讨论】:

  • 您在基类中的notify 应该是virtual,否则这看起来不错
  • 谢谢。我最近写的Java太多了。
  • 我更喜欢使用带有委托的适配器而不是继承,并提供一个狭窄的控制接口而不是通用的观察者,但否则这就是我要走的路(c:
  • @BillyONeal:不一定,-Wdelete-non-virtual-dtor 无论如何都会警告任何问题。
  • @PeterWood 我很乐意详细说明您提出的建议,也许可以作为另一个答案。
【解决方案2】:

听起来您正在寻找观察者模式。

在观察者模式中,一个对象维护一个依赖列表(又名观察者),并根据需要通知他们更改。在您的情况下,Control 类是观察者,Child 类是通知者。

Boost 库有一个信号/槽机制,可用于实现这一点: http://www.boost.org/doc/libs/1_49_0/doc/html/signals/tutorial.html#id3137144

有一个在 SO 上使用这个的例子: Complete example using Boost::Signals for C++ Eventing

【讨论】:

  • 有趣的想法;不过,我原以为会有一种方法可以用核心语言做一些事情。
  • 还没有,但我听说它将在 C++14 中讨论。
  • 查看其他答案,了解 C++ 中没有提升的观察者模式示例
  • 如果您要使用它的目的不是太复杂并且不会改变太多,我建议您查看 Mikkel 对纯粹“核心”实现的回答
  • 顺便说一句,有一些区别......在Boost版本中,我相信信号/插槽机制完全使用模板来实现,即没有继承。所以没有运行时开销。这对您来说可能很重要,也可能无关紧要。
【解决方案3】:

提出另一个想法,Friend class 似乎也可以在这方面大有帮助。

儿童不应访问的方法为privateprotected。这样孩子就不会造成任何你不希望它造成的伤害。

然后,如果您需要另一个类来管理您的父控制器,则您的父类将另一个类设为 friend,以便它可以访问您对子控制器保留的所有私有方法。

【讨论】:

    【解决方案4】:

    当我阅读有关程序设计模式等的一般主题时,似乎并不特别推荐这种组织对象的方法。

    这是错误的,因为inversion of control。您可以将其创建为Child 的成员变量。 Martin Fowler 有一个very nice article 解释了一种控制反转(依赖注入)。

    下面是一个简单的例子解释如何实现依赖注入(通过构造函数注入依赖):

    #include <iostream>
    
    struct B
    {
    
        virtual ~B(){}
        virtual void foo() = 0;
    };
    struct A1 : public B
    {
        virtual ~A1(){}
        virtual void foo()
        {
            std::cout<<"hello"<<std::endl;
        }
    };
    struct A2 : public B
    {
        virtual ~A2(){}
        virtual void foo()
        {
            std::cout<<"test"<<std::endl;
        }
    };
    
    struct C
    {
        C( B &b_ ) : b(b_){}
    
        void bar()
        {
          b.foo();
        }
    
        B &b;
    };
    
    #define SAY_HI
    
    int main()
    {
    #ifdef SAY_HI
        A1 a;
    #else
        A2 a;
    #endif
        C c(a);
    
        c.bar();
    }
    

    如果您考虑设计模式,您会注意到其中一些使用了某种控制反转。以下是一些众所周知的(来自this link):


    顺便说一句,你的名字叫chain of responsibility

    【讨论】:

    • 基本上我所展示的是依赖注入,所以当你说我“错误地”做这件事时,你能解释一下你的意思吗?您的示例也是通过构造函数进行的注入。这也包含在我的示例中。
    猜你喜欢
    • 2018-09-03
    • 1970-01-01
    • 1970-01-01
    • 2016-06-09
    • 2014-03-31
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多