【问题标题】:A dependency loop依赖循环
【发布时间】:2013-07-17 07:47:44
【问题描述】:

我设计了一个继承自CDialog的对象(称为NBDialog,以及一些控件的派生对象,如CEditCDateTimeCtrlCComboBox等。
NBDialog是一个项目,控件在其他项目中。

当然,所有的控件都放在对话框上并使用对话框的方法,所以我必须 #include NBDialog.h,并为链接器添加其.lib 文件。

我还想处理对话框中的所有这些控件,所以我在NBDialog.h 中写了以下几行:

class NBCommonEditBox; 
class NBDateTimeCtrl;
class NBCommonComboBox;

CMapWordToOb* NBGetEditBoxMap();
NBCommonEditBox* NBGetEditBoxById(unsigned long ID);

CMapWordToOb* NBGetDateTimeMap();
NBDateTimeCtrl* NBGetDateTimeById(unsigned long ID);

CMapWordToOb* NBGetComboBoxMap();
NBCommonComboBox* NBGetComboBoxById(unsigned long ID);

这种方式NBDialog.h 不知道对象的上下文,但它知道它们存在并将它们存储在映射中。

现在我想扩展NBDialog项目并添加一个方法来获取所有控件的打印信息,这样所有继承自NBDialog的对象都可以使用这个方法。打印信息在控件实现中定义。

编辑:如果我在NBDialog.cpp 中编写此方法,我无法编译它,因为NBDialog 不知道控件类的上下文:

CStringList* NBDialog::NBGetMainTexts()
{
    CStringList* mainTexts = new CStringList();

    POSITION pos;
    WORD key;
    NBCommonEditBox* currEdit = NULL;
    for (pos = this->NBGetEditBoxMap()->GetStartPosition(); pos != NULL;)
    {
        this->NBGetEditBoxMap()->GetNextAssoc(pos, key, (CObject*&)currEdit);
        currEdit->NBStringsToPrint(mainTexts);
    }
    return mainTexts;
}

有没有办法写出想要的方法?

【问题讨论】:

  • 您可以使用多重继承为这些对象添加一个接口,并强制转换为该接口,您可以提供一个虚拟函数来执行您需要的查询。我不确定这是否能回答你的问题,因为理解你真正想要做什么有点困难。你能展示一些你的函数的伪代码来迭代你的对象吗?你真的需要一个 ID->CWnd 的映射来迭代吗?
  • @Pete 我添加了一个伪代码。现在轮到你了……
  • 为什么会得到未解析的符号?您确定实现这些虚拟方法的对象已链接吗?
  • @greatwolf 他们不能被链接,正如我在问题中解释的那样。如果我链接它们,它将是依赖循环。
  • 好的,我明白你的意思,Pete 的建议是有道理的。但老实说,我认为NBDialog 不应该为此负责。如果稍后添加一种新的控件怎么办?您最终将返回 NBDialog.h 以添加另一个 NBGet*Map 函数。

标签: c++ inheritance dependencies containers circular-dependency


【解决方案1】:

最简单的方法是为此定义一个接口并添加该接口而不是 CObject。该接口可以提供一种方法来获取控件本身。不要害怕多重继承——是的,它可能会有轻微的性能损失,但这对你来说不会是一个问题。在这种情况下,它类似于 Java 中的接口继承,因为您将使用纯接口。

您也可以以类似的方式实现这一点,以避免多重继承,但它增加了您不需要的更多复杂性。

// Interface goes in the NBDialog project
class INBControl {
public:
    virtual ~INBControl() = 0;
    virtual CWnd* getWnd() = 0;
    virtual void getStringsToPrint(CStringList& strings) = 0;
};
inline INBControl::~INBControl() {}

class NBCommonComboBox : public CComboBox, public INBControl
{
public:
    // ... stuff ...
    virtual CWnd* getWnd() {
        return this;
    }
    virtual void getStringsToPrint(CStringList& strings) {
        strings.AddTail("foo"); // for example
    }
};


// NBDialog
    #include <map>
class NBDialog : public CDialog
{
public:
    // .. stuff ..
private:

        typedef std::map<int, INBControl*> ControlMap;
        ControlMap control_map_;
};

void NBDialog::addNBControl(INBControl* control, int id)
{
    CWnd* wnd = control->getWnd();
    // Do stuff with the control such as add it
    control_map_[id] = control;
}

// let the caller be responsible for [de]allocation of the string list
void NBDialog::NBGetMainTexts(CStringList& texts) 
{
    ControlMap::iterator i = control_map_.begin();
    ControlMap::iterator e = control_map_.end();

    for(; i != e; ++i) {
        i->second->getStringsToPrint(texts);
    }
}

或者使用自定义窗口消息并迭代所有控件,向下转换为 CWnd 并在其 HWND 上使用 SendMessage。每个控件都需要处理您的自定义窗口消息。您可以在消息的 LPARAM 中传递一个指向字符串列表的指针。这种方法很灵活,但有些脆弱/不安全,如果您最终意外使用相同的消息 ID 来处理其他事情,可能会崩溃。

【讨论】:

  • 非常感谢!当我有时间时,我会在当前效率低下的代码中进行更改。
【解决方案2】:

您的实现文件 (NBDialog.cpp) 对 #include 提供必要的标头以使其工作(大概是 NBCommonComboBox.h 等)因为 .cpp 文件不是 #include 由任何你不会引起任何循环的东西都包括问题。

【讨论】:

  • 我也是这么想的,但是失败了。当我编译 NBDialog 项目时,它会向继承控件的所有方法发送未解析的外部符号消息。
  • 当您说“项目”时,我确信这意味着您正在使用某种 IDE,它以我无法想象的方式管理依赖项。因此,您需要弄清楚如何在 IDE 中确保您在链接时具有可用于链接器的正确依赖项,但这不是您的源代码的问题 - 您必须能够链接库包含您尝试使用的符号。
  • 现在我注意到未解析的外部符号只是虚函数。它们在基类中也有实现,但派生类可能必须使用其他实现。
猜你喜欢
  • 2010-09-12
  • 2021-10-02
  • 1970-01-01
  • 2012-04-24
  • 2014-05-19
相关资源
最近更新 更多