由一个贴子引发的讨论

查看MFC中与CListCtrl和CListView相关的源码时发现,CListView派生自CWnd->CView->CCtrlView,而CListCtrl则直接派生自CWnd类,而CListView::GetListCtrl函数居然仅仅用了一句 return *(CListCtrl*)this; 将CListView活生生的转换成了CListCtrl,百思不得其解,后在CSDN论坛中向众高手求助,终于明白了个中原由。

 

 

众所周知,C++的多继承是个极其讨厌的东西,稍有不慎就会带来灾难
  尤其是钻石型多继承:
        A
      ↑ ↑
      B   C
      ↑ ↑
        D
  (这个图有点难看,不好意思)
  查看MFC源码,多继承的类非常少(嗯,M$那帮程序员看来也害怕多继承),但是却有很多类间的关系带有明显的多继承特征:
  1)CEdit继承自CWnd
  2)CEditView继承自CCtrlView,CCtrlView继承自CWnd
  实际使用中显然可以发现CEditView有很多CEdit的行为,难道CEditView重复了CEdit的代码?其实不然
  下面这篇文章转自微软的网站,可以发现其中一个很聪明的技巧,借此技巧也可以告诉我们如何实现自己的CtrlView

 

 

  Q
    I made a custom control derived from CWnd, and now I want to use it as a view. My first solution was to embed the control into the view and handle OnSize in the view to position the control over the client area. The problem is that mouse messages go to the control and cannot be overridden in the view. The keystroke messages go to the view and must be manually forwarded to the control.
    I read about CCtrlView as a base class for common controls. I've even managed to write the view around it (I believe that you wrote about this in an issue of MSJ), but I could not get it to work with my CWnd-based control. Can this be done, and how?
                        Mateo Anderson

 

 

 

  A
    CCtrlView is a trick that MFC uses to convert control classes to view classes. For example, from CTreeCtrl to CTreeView and from CListCtrl to CListView. The comment in the documentation for CCtrlView says, "CCtrlView allows almost any control to be a view." Unfortunately, the "almost" is a bit of an exaggeration, unless by "any control" the author was thinking about any of the built-in Windows? controls like CEdit, CTreeCtrl, and so on. CCtrlView uses a trick that works only in special circumstances.
    To understand how CCtrlView works, let's take a look at CTreeView, which is derived from CCtrlView. There are three important functions to consider: the constructor, PreCreateWindow, and GetTreeCtrl.
    The constructor tells CCtrlView which kind of Windows control to create.

 

 

 

CTreeView::CTreeView() :
  CCtrlView(WC_TREEVIEW, dwStyle)
{
}

 

 

 

    In this case, WC_TREEVIEW (#defined in commctrl.h) is the name of the (Windows) tree control class: namely, SysTreeView32. CCtrlView stores this name in a data member for later use.

 

 

 

CCtrlView::CCtrlView(LPCTSTR lpszClass,
  DWORD dwStyle)
{
  m_strClass = lpszClass;
  m_dwDefaultStyle = dwStyle;
}

 

 

 

    The next function that comes into play is PreCreateWindow, which CTreeCtrl inherits from CCtrlView. CCtrlView::PreCreateWindow uses m_strClass to set the class name in the CREATESTRUCT just before the window is created.

 

 

 

// CCtrlView uses stored class name
BOOL CCtrlView::PreCreateWindow(CREATESTRUCT& cs)
{
  cs.lpszClass = m_strClass;
  ???
  return CView::PreCreateWindow(cs);
}

 

 

 

    Now the window created is of the desired class—in this case, SysTreeView32. So far, so good. But if CTreeCtrl is derived from CCtrlView, which is derived from CView, how can it also be derived from CTreeCtrl, the MFC class that wraps the tree control? CTreeView and CTreeCtrl are completely independent, with different inheritance chains. CTreeCtrl is derived from CWnd directly, whereas CTreeView is derived from
CCtrlView/CView! This is where the trick comes in.
    To manipulate the tree view as a tree control, CTreeView provides a special function, GetTreeCtrl, to get the tree control.

 

 

 

CTreeCtrl& CTreeView::GetTreeCtrl() const
{
  return *(CTreeCtrl*)this;
}

 

 

 

    GetTreeCtrl simply casts the CTreeView to a CTreeCtrl. But wait a minute—how on earth can this work? The two classes are entirely different, with different data members and virtual function tables—you can't just cast one class to another and expect it to work!
    The answer is that CTreeCtrl has no virtual functions and no member data. You could call it a pure wrapper class. CTreeCtrl doesn't add anything (data or virtual functions) to its base class, CWnd; all it adds is a bunch of wrapper functions, concrete functions that send messages to the underlying HWND. For example:

 

 

 

HTREEITEM CTreeCtrl::InsertItem(...)
{
  return (HTREEITEM)::SendMessage(m_hWnd,
    TVM_INSERTITEM, ...);
}

 

 

 

    The only data member that InsertItem accesses is m_hWnd, which all CWnd-derived classes have. InsertItem and all the other wrapper functions simply pass their arguments to the underlying HWND, converting C++-style
member functions to Windows-style SendMessage calls. The object itself ("this" pointer) could be an instance of any CWnd-derived class, as long as m_hWnd is in the right place (that is, the first data member of the class) and the HWND is, in fact, a handle to a tree control. It's the same reason you can write

 

 

 

pEdit = (CEdit*)GetDlgItem(ID_FOO);

 

 

 

    even though GetDlgItem returns a pointer to a CWnd, not a CEdit: because CEdit is also a pure wrapper class, with no extra data or virtual functions beyond what it inherits from CWnd.
    So the "almost any" in the statement "CCtrlView allows almost any control to be a view" means specifically any control that adds no member data and no virtual functions to CWnd, what I am calling a "pure wrapper class." If your control class has its own data or virtual functions, you can't use CCtrlView because the extra data/virtual functions won't exist in CCtrlView/CView.
    For example, the first virtual function in CView is CView::IsSelected. If your control class has some other virtual function, then things will certainly bomb when you cast CCtrlView to your CFooCtrl and try to call that virtual function. The function simply doesn't exist. Likewise, the first data member in CView is m_pDocument. If your control class expects some other data member, your code will bite the bag when it tries to access it, if the object called is really a CCtrlView, not a CFooCtrl. Too bad, so sad.
    In short, the only time you can use the CCtrlView trick is when your CWnd-derived control class has no virtual functions and no member data of its own. C'est la vie.
    If you want to use your control in a doc/view app, what can you do—throw your head on the table and weep? Of course not! Your first approach was dandy: create your control as a child of the view and use OnSize to position it exactly over the view's client area.

 

 

 

CFooView::OnSize(..., cx, cy)
{
  m_wndFooCtrl.SetWindowPos(NULL,
    0,0,cx,cy,SWP_NOZORDER);
}

 

 

 

    Those input problems you encountered are easily overcome. Consider the mouse. If you want to let the parent view handle mouse messages sent to your control, the thing to do is abstract the messages into higher-level events. That's a highfalutin way of saying something familiar to us all.
    Consider, for example, a button. When the user clicks a button, the button notifies its parent with a BN_CLICKED event. It does not send WM_LBUTTONDOWN; it sends a WM_COMMAND message with subcode = BN_CLICKED.
    The button is telling its parent window: the user clicked me. Likewise, list controls don't broadcast WM_LBUTTONDOWN; they do a little processing and notify their parents with LBN_SELCHANGE. (In the case of a double-click, list controls do propagate LBN_DBLCLK, which is little more than WM_LBUTTONDBLCK.) In general, the idea is that controls convert raw events into higher-level events that are meaningful in the context of the control.
    If you're doing this at home, you should probably use the more modern way, which is WM_NOTIFY, instead of WM_COMMAND. WM_NOTIFY lets you pass a whole struct of information instead of trying to squish everything into half a DWORD. You can decide which mouse messages your control should propagate.
    For example, buttons don't normally send BN_DOUBLECLICKED unless they have the BS_NOTIFY style.
    So much for mousing. Now, what about the keyboard? That's even easier. When the user activates your app by clicking on the caption or Alt-TABing to it, Windows normally gives focus to the main frame. MFC, in turn, passes focus to your view:

 

 

 

void CFrameWnd::OnSetFocus(...)
{
  if (m_pViewActive != NULL)
    m_pViewActive->SetFocus();
  else
    CWnd::OnSetFocus(...);
}

 

 

 

    All you have to do is pass the focus, in turn, to your control:

 

 

 

CFooView::OnSetFocus(...)
{
  m_wndFooCtrl.SetFocus();
}

 

 

 

    Now keystrokes go directly to your control. Do not pass view. I told you it was easy! This is the age-old Windows way of doing things, but with all those frameworks doing so much for you nowadays, it's easy to miss the basics.
    The upshot is this: if your custom control view class is not a pure wrapper function, that is, if it has so much as one data member or virtual function of its own, then the way to convert your control into a view is to instantiate it as a child window of the view and integrate it in three simple steps.
    Handle WM_SIZE in the view to position your control exactly over the view's client area.
    Convert mouse messages in the control to higher-level parent WM_NOTIFY notifications.
    Handle WM_FOCUS in the view to set focus to your control.
    Incidentally—if I may be permitted to muse for just a paragraph or two—this example illustrates one of the drawbacks of the MFC object model, which doesn't allow multiple inheritance. You can't say, "my class is a view and a foo control," which is really what you want to do. It also shows why some programmers may choose to implement custom controls using C only, and not C++. It is possible, you know. All you have to do is register your own window class (in the Windows sense), allocate a little memory block to hold your window's instance data, and implement all your functions as messages—WMFOO_GETTHIS and WMFOO_SETTHAT. This was the only way to implement custom controls before C++ came along, and it still has many benefits.
    For example, if you do it this way, some other C++ programmer could come along and write a C++ pure wrapper class for your window, with simple wrapper functions CFooCtrl::GetThis and CFooCtrl::SetThat, which merely passed the parameters to and fro using the proper WMFOO_XXX messages, and then such a programmer could in fact use CCtrlView to convert your control to a view! Or, to put it differently, one way to use CCtrlView is to reimplement your custom control using pure C and the Windows API with messages and subclassing instead of MFC! This would require a bit more typing and type-casting (for all those WPARAMs and LPARAMs), but would leave you feeling satisfied and pure.
感悟:
  指针的类型是可以强制转换的
### 回答1: 好的,这是一篇计算机技术分享帖子的示例: 《使用计算机视觉技术进行图像分类》 在这篇帖子中,我将介绍如何使用计算机视觉技术来对图像进行分类。计算机视觉是一门涉及机器学习、深度学习和图像处理的技术,可以让计算机“看”到并理解图像中的内容。 首先,我们会介绍图像分类的基本概念,包括什么是图像分类,它的应用场景有哪些,以及如何使用机器学习算法进行图像分类。 然后,我们将深入介绍几种常见的图像分类模型,包括卷积神经网络 (CNN)、循环神经网络 (RNN) 和自编码器 (AE)。我们还会介绍如何使用这些模型进行训练和预测,以及如何使用数据增强技术来提高模型的泛化能力。 最后,我们会分享一些实际案例,展示如何使用计算机视觉技术解决各种实际问题,包括图像分类、目标检测、语音识别等。 通过学习本帖子,你将能够了解 ### 回答2: 当然!下面是一篇关于计算机技术的分享帖子: 标题:探索人工智能在医疗技术中的应用 大家好!今天我想和大家分享一下人工智能在医疗技术中的应用。随着技术的不断发展,人工智能在医疗领域发挥着越来越重要的作用,为医生提供更准确、高效的医疗诊断和治疗方案。下面,我将为大家介绍几个目前在医疗技术中广泛应用的人工智能技术。 首先,人工智能在医学图像诊断中发挥着关键作用。通过深度学习算法,可以对医学图像进行自动分析和识别,帮助医生更准确地诊断疾病。例如,针对早期癌症的检测,人工智能可以提供更准确的筛查结果,帮助尽早发现病变,从而提高治愈率。 其次,人工智能在病历数据分析中的应用也越来越受欢迎。通过处理庞大的病历数据,人工智能可以帮助医生快速找到潜在的疾病模式和关联,从而为制定个性化的治疗方案提供支持。人工智能还可以预测疾病的风险,提前做好干预措施,有效降低疾病的发病率。 此外,人工智能在手术辅助系统中也有广泛应用。通过模拟手术过程和精确的定位,人工智能可以提供精确的手术引导,减少手术风险并提高手术成功率。例如,机器人辅助手术系统可以通过准确的定位和智能的操作,提高手术精确度,缩短手术时间,减少患者的恢复时间。 综上所述,人工智能在医疗技术中的应用前景广阔。它不仅可以提高医疗服务的质量和效率,还能够帮助医生提供更准确、个性化的诊断和治疗方案。相信未来,在人工智能的帮助下,我们将迎来更加智能和人性化的医疗世界。 感谢大家的聆听,希望我今天的分享对大家有所帮助。如果你对这个话题有任何疑问或想要了解更多,请随时在评论区留言。谢谢! ### 回答3: 标题:探索人工智能在计算机视觉中的应用 正文: 近年来,人工智能(Artificial Intelligence,AI)技术在各个领域都得到了广泛的应用,特别是在计算机视觉方面。计算机视觉是指通过计算机和相关技术对图像和视频进行处理与分析的能力。本文将探索人工智能在计算机视觉中的应用以及带来的潜在影响。 首先,人工智能为计算机视觉领域带来了突破性的进展。通过深度学习和神经网络等技术,计算机能够识别和理解图像中的对象,并实现多样化的注释和分类。例如,人脸识别技术的发展使得人员身份验证和安全监控领域取得了重大突破。此外,AI在医疗图像分析、自动驾驶和机器人视觉等领域也得到了广泛应用,大大提高了效率和准确性。 其次,AI还为计算机视觉领域带来了新的挑战和机遇。尽管现在的人工智能技术已经能够实现一定程度的视觉理解,但仍然存在一些问题和限制。例如,对于复杂的图像场景和遮挡物的处理仍然具有挑战性。此外,如何在保护隐私和确保公平性的前提下使用人脸识别技术等AI应用也是一个亟需解决的问题。 最后,人工智能的发展也引发了对计算机视觉伦理和社会影响的讨论。随着AI在个人隐私、就业和社会伦理等方面的重要应用,我们需要思考如何确保技术的公正和透明,并制定相关的政策和法规。此外,积极推动相关技术的研发和应用,培养专业人才,也是保持竞争力和推动社会进步的关键。 综上所述,人工智能在计算机视觉中的应用带来了许多机遇和挑战。通过不断的研究和创新,我们可以更好地利用这一技术,推动计算机视觉的发展,并加以合理的伦理和社会规范。这将为我们的社会带来更多的便利和进步。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值