【问题标题】:Why does *everything* use a template base class in ATL/WTL?为什么 *everything* 在 ATL/WTL 中使用模板基类?
【发布时间】:2012-05-03 16:32:26
【问题描述】:

我很难理解 ATL/WTL 代码中模板的用途。

当您查看 WTL 时,您会看到如下代码:

template <class TBase>
class CEditT : public TBase
{
    ...
};

typedef CEditT<ATL::CWindow> CEdit;

为什么CEditT 用模板基类定义?

换句话说,在T 不是 CWindowCEditT&lt;T&gt; 的情况下,CEditT&lt;T&gt; 会被实例化吗?

【问题讨论】:

  • 也许是为了适应您想提供自己的 Window 实现的情况?
  • @Prætorian:你如何/为什么/什么时候想要/需要/这样做?
  • 不知道:-)。也许您实现了一些非常精美的皮肤,并希望您的所有窗口都具有该皮肤。另外,这不是真正的 CRTP,是吗?如果它是CRTP,CEdit 将是class CEdit : public Base&lt;CEdit&gt; { ... };
  • @Prætorian:但这对于像 CEdit 这样的东西来说似乎真的很奇怪......而且,我想我在写“CRTP”时正在考虑CWindowImpl。我会删除它,谢谢指出。

标签: c++ winapi visual-c++ atl wtl


【解决方案1】:

这样您就可以覆盖ATL::CWindow 中由ATL/WTL 类调用的方法。如果您不喜欢 ATL::CWindow 中的某些内容,您可以使用重写的方法从 ATL::CWindow 派生一个类,然后将您的新类作为 TBase 传递。

例如,ATL::CWindow::CenterWindow 很长一段时间以来都有一个错误,即它没有正确考虑多个监视器,我看到人们使用这种技术来覆盖 CenterWindow 并通过一个固定的实现。

【讨论】:

  • +1 一个实际的例子,谢谢。但是有一个问题:这不是已经可以用模板化的 subclass 解决了吗? (例如FixedWindow&lt;CEdit&gt; 而不是CEditT&lt;FixedWindow&gt;)
  • @Mehrdad:如果 CEdit 是调用违规函数的东西,FixedWindow&lt;CEdit&gt; 将如何提供帮助?也许我不明白你的意思。
  • 哦,是的,它不会,我的错。我在想其他人调用该函数,而不是类本身。
【解决方案2】:

假设使用了合成:

template <class TBase> class CEditT {
public:
  TBase base;
  ...
};

这和类似的东西没有太大区别:

template <class ITEM> class ListNode {
public:
   ITEM item;
   ListNode<ITEM> *next;
   // ...
};

ListNode 可以改为继承ITEM,就像CEditT 与TBase 一样,但缺点是ITEM 不能是像int 这样的基本类型。另一方面,ListNode 将成为一种ITEM,这可能很有用。此外,它可以访问ITEM 的任何protected: 部分。

【讨论】:

  • 我想我的问题是,为什么它不直接从CWindow 继承?
  • 好问题。这样它就不会硬编码到特定的窗口类实现,就像ListNode 不绑定到特定类型的ITEM。例如,您可以通过自定义标准窗口实现自己的窗口。从中派生出MyWindow,然后您可以实例化CEditT&lt;MyWindow&gt;。问题可以反过来:当我们可以将实用程序类模板化并将其定位到具有正确接口的种类目标类时,为什么我们要将实用程序类绑定到特定目标类?这是一个范式。 ;)
  • 在什么情况下(实际上来说)CEditT&lt;T&gt; 会在T 不是 CWindow 的地方被实例化? (这有什么已知的用例吗?)
  • 嗯,好问题。即使没有,基本上也没有什么好理由不让CEditT 与框架不一致。某个地方的某个人会想要CEditT&lt;CWindowImpl&lt;MyClass&gt;&gt;,然后会意外地找到一个单一的CEdit。
  • 这里有一个雅虎群组就这个主题发帖:tech.groups.yahoo.com/group/wtl/message/5500
【解决方案3】:

这样它就可以利用Curiously Recurring Template Pattern。如果您创建CEditT 的派生类,您的定义将是class CMyEdit : public CEditT&lt;CMyEdit&gt;。通过将其this 指针静态转换为您的类,CEditT 可以调用您的方法,而无需使用 vtable。

【讨论】:

  • 什么时候真正有用(对于CEditT 等)?
  • @Mehrdad,看一眼 CEditT 的来源可能会回答这个问题。我现在无法访问它,否则我会自己做。很可能它使用 CRTP 只是为了与所有其他 ATL 类保持一致。
  • 这实际上并没有回答问题。这与 CRTP 无关。这个问题是关于将 base 类模板化,而不是将派生类模板化为static_cast。
【解决方案4】:

为什么everything在ATL/WTL中都使用模板基类?

简而言之,为了实现一种“奇怪形式的编译时多态性”,这要归功于一种设计模式(更像是 C++ 习惯用法),人们可能会称之为“颠倒继承” 作为“指定基类的基类”的技术,以便“允许您将自己的类插入到类层次结构中”以实现专门类型的灵活性“没有对任何特定派生类的硬编码实现”。

在阅读了Jim Beveridge 撰写并于 1999 年 7 月由 Visual C++ Developers Journal 发表的优秀文章 ATL and Upside-Down Inheritance - Understanding ATL's atypical design approach 之后,这一切都应该清楚了。这里是 link to full not paginated copy of the same article(也在 Wayback Machine 中)。

【讨论】:

    猜你喜欢
    • 2011-12-10
    • 2023-04-09
    • 2011-02-04
    • 1970-01-01
    • 1970-01-01
    • 2011-06-25
    • 2011-12-16
    • 2012-07-04
    • 2012-08-05
    相关资源
    最近更新 更多