欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
CodeGuru: Adding Behavior to Classes, Part I ...
Adding Behavior to Classes, Part I - An Introduction
Rating: none

Roger Onslow (view profile)
April 16, 2001

First, let's consider how one can go about adding new functionality to a group of classes. The most obvious, brute-force way to add the same functionality to a number of classes is to simple copy and paste the code into each one. This is far from an ideal solution- you only want to write the code once and have a single copy of it to maintain.

Modifying the Base Class

If we were creating our own class hierarchy and wanted to add some functionality to all of our derived classes, then the obvious place to add it is the base class. If the details of how to implement the behaviour need to be different for the derived classes, then we would use virtual functions to override the required implementation.

Let's take a simple class hierarchy:
(continued)



  Heroes Happen Here

New products, new technologies: Microsoft SQL Server 2008, Microsoft Visual Studio 2008, and Windows Server 2008 create new opportunities to use your existing skills and to grow your business. ?


  Tips for Growing Your Microsoft Practice

You can grow your practice by increasing business with existing clients or adding more customers, expanding your practice areas or expanding your business into hosted or managed services. Discover which approach to growing your business makes the most sense for you and your customers. ?


  Drive Your Business with Microsoft

Explore the business opportunities available through the Microsoft Partner Program, which is designed to help you generate leads, drive customer demand and sales, increase your profitability, and assess your business performance. ?
  Partner-to-Partner Networking: A New Paradigm in Relationship Building

Heres a great way to build your business: Team up with partners whose solutions and services work as an extension of your own offerings. View this Webcast to learn how building networks with other partners can help your business grow. ?
  IT Provider Grows Profits 85% in One Year with Microsoft Technologies

Knight Enterprises Inc., a longtime Novell partner, chose to add Microsoft technologies to its offerings after attending a Microsoft partner event. The company's major accounts soon followed suit, opting for Microsoft solutions over those of the competition. Download this case study to discover how Knight now expects its revenue to double in two years time. ?

This gives you pseudocode that is something like this:

class Base {};class Derived1 : public Base {};class Derived2 : public Base {};

Add a virtual NewFunction() to the base class:

class Base {public:virtual void NewFunction();};class Derived1 : public Base {// no change, just gets Base::NewFunction()};class Derived2 : public Base {public:// override the new member functionvirtual void NewFunction();};

NewFunction() is now available in both class Derived1 and class Derived2. Furthermore, the implementation of NewFunction() has been overridden for class Derived2.

This is fine if we are writing the entire class hierarchy ourselves and can change the source code as we see fit. However, when it comes to MFC, we are working with an existing class library. We cannot go adding new member functions to the CWnd base class. We cannot even change the other classes in the existing hierarchy. And that means this approach will not work.

Multiple Inheritance

Another way of adding functionality is to use multiple inheritance so we can add functionality to derived classes without modifying the base class. We put the new functionality into a separate base class and then change the declarations of the derived classes so they derive from this new class.

To make things more familiar, I will use MFC class names in the diagrams and code snippets. The base class will be CWnd, and our two derived classes CListCtrl and CTreeCtrl. We cannot change any of these classes directly, as they are part of the MFC class library. However, we can derive our own classes from them as shown:

This gives you code that is something like this:

// library classes .. we don't touch theseclass CWnd {};class CListCtrl : public CWnd {};class CTreeCtrl : public CWnd {};// my classesclass CMyListCtrl : public CListCtrl {};class CMyTreeCtrl : public CTreeCtrl {};
To add extra functionality, we create a new class called CAddBehaviour and use multipleinheritance for our own derived classes. We do not need to make any changes to the library classes.

// library classes .. we don't touch theseclass CWnd {};class CListCtrl : public CWnd {};class CTreeCtrl : public CWnd {};// new behaviourclass CAddBehaviour {public:virtual void NewFunction();};// my classesclass CMyListCtrl : public CListCtrl, public CAddBehaviour {};class CMyTreeCtrl : public CTreeCtrl, public CAddBehaviour {public:virtual void NewFunction();};

There is a slight catch here, however. If the implementation of CAddBehaviour::NewFunction() requires access to the members of CWnd--to do anything useful, it probably will--then we are out of luck as things stand because we cannot derive CAddBehaviour from CWnd. There is a trick we can use here to help get around this obstacle. We can store a pointer to a CWnd object in the CAddBehaviour class.

class CAddBehaviour {public:CAddBehaviour(CWnd* pWnd): m_pWnd(pWnd) {// body of constructor}virtual void NewFunction();protected:CWnd* m_pWnd;};class CMyListCtrl : public CListCtrl, public CAddBehaviour {public:// change the constructor for CMyListCtrlCMyListCtrl(): Base(), CAddBehaviour(this) {// body of constructor}};// similarly for CMyTreeControl

Let's look at how a CMyListCtrl object would be laid out in memory: In addition to the specific data for CMyListCtrl, there are a CListCtrl part (which includes the CWnd base class) and a CAddBehaviour part.

When an object of CMyListCtrl is constructed, both its CListCtrl part and its CAddBehaviour part are also constructed. The constructor initialises the pointer in the CAddBehaviour part to point to itself. In particular, to its CWnd part. This means that CAddBehaviour::NewFunction() can use m_pWnd to call public members of CWnd.

The only remaining catch is that NewFunction() can only access public members of CWnd. If we could change CWnd and declare CAddBehaviour to be a friend, then all would be well; we can't do that because the whole reason for using multiple inheritance in the first place is so that we do not need to change the base CWnd class.

But, as always, there is a solution: Derive a class from CWnd that does have CAddBehaviour as a friend. Then we can use that to get at protected members of CWnd. That would make our code look like this:

class CAddBehaviourFriend : public CWnd {friend class CAddBehaviour;};class CAddBehaviour {public:CAddBehaviour(CWnd* pWnd): m_pWnd((CAddBehaviourFriend*)pWnd) {// body of constructor}virtual void NewFunction();protected:CAddBehaviourFriend* m_pWnd;};class CMyListCtrl : public CListCtrl, public CAddBehaviour {public:// change the constructor for CMyListCtrlCMyListCtrl(): Base(), CAddBehaviour(this) {// body of constructor}};// similarly for CMyTreeControl

Message Maps

Well, this seems like a reasonable solution. With minimal change we can add new functionality to any or all of our derived classes. But all is still not well. MFC throws a big spanner in the works with Message Maps. Now, don't get me wrong, Message Maps are wonderful things, but there are some limitations. There can be only a single message map at each level of the hierarchy, and the functions called and the class that owns the message map must have CWnd ancestry. That means we cannot put up a message map in our CAddBehaviour class. Nor can we put a message map in the CMyListCtrl class and have it directly call member functions of CAddBehaviour.

Well, this is fairly easy to get around. We put the message map in CMyListCtrl class and call member functions of CmyListCtrl, which in turn call member functions of CAddBehaviour such as NewFunction(). This would mean we have code something like this:

class CMyListCtrl : public CListCtrl, public CAddBehaviour {public:// change the constructor for CMyListCtrlCMyListCtrl(): Base(), CAddBehaviour(this) {// body of constructor}protected://{{AFX_MSG(CMyListCtrl)afx_msg void OnSomeMe ssage();//}}AFX_MSGDECLARE_MESSAGE_MAP()};BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)//{{AFX_MSG_MAP(CMyListCtrl)ON_COMMAND(ID_SOMETHING,OnSomeMessage);//}}AFX_MSG_MAPEND_MESSAGE_MAP();void CMyListCtrl::OnSomeMessage() {NewFunction();}// similarly for CMyTreeControl

The only problem now is that we now need to add multiple inheritance, a new constructor, a message map, and some wrapper functions that call CAddBehaviour for every class that we want to have our new behaviour. This is almost as much work to get right as manually adding the behaviour to each class individually. So, it doesn't look like all this has helped much.

Template Classes

There is another method of adding behaviour that doesn't involve multiple inheritance. In fact it is very much like manually adding code to each class. This method is templates. To add functionality using a template, one usually slots a template class between a base class and a derived class. The template class has a template parameter that specifies a base class and adds extra functionality to it. You then derive your class from an instance of the template class with the appropriate base class filled in. This is somewhat different from multiple inheritance, as shown below:
// Multiple-inheritance...class Base {};class Extra {public:virtual void NewFunction();};class Derived : public Base, public Extra {// gets Extra::NewFunction()};// Template class...class Base {};template <class BASE>class Extra : public BASE {public:virtual void NewFunction();};class Derived : public Extra<Base> {// gets Extra<Base>::NewFunction()};

This is illustrated in the class diagram below:

One problem with templates is that their implementation usually has to be exposed to the world with inline member function definitions. Also, you end up with separate instances of each function for every different base class you use, which can result in code bloat.

The Best of Both Worlds

One can combine the shared code and encapsulation of multiple inheritance with the ease of use of templates to get a good solution with the advantages of both methods.
// Multiple-inheritance...class Base {};class ExtraBase {public:virtual void NewFunction();};template <class BASE>class Extra : public BASE, public ExtraBase {};class Derived : public Extra<Base> {// gets Extra<Base>::NewFunction()};

This is illustrated in the class diagram below:

By using the combination of multiple inheritance and templates we should be able to improve on our previous solution. The template class can do the work of adding multiple inheritance, fixing constructors, and adding wrapper functions and message maps for us so we don't need to do this for every derived class.

This means we end up with code something like this:

template <class BASE>class TAddBehaviour : public BASE, public CAddBehaviour {public:// change the constructor for TAddBehaviourTAddBehaviour (): BASE(), CAddBehaviour(this) {// body of constructor}protected://{{AFX_MSG(TAddBehaviour)afx_msg void OnSomeMessage();//}}AFX_MSGDECLARE_MESSAGE_MAP()};BEGIN_MESSAGE_MAP(TAddBehaviour, BASE)//{{AFX_MSG_MAP(TAddBehaviour)ON_COMMAND(ID_SOMETHING,OnSomeMessage);//}}AFX_MSG_MAPEND_MESSAGE_MAP();void TAddBehaviour::OnSomeMessage() {NewFunction();}class CMyListCtrl : public TAddBehaviour<CListCtrl> {};// similarly for CMyTreeControl

You can see that the template class now does all the work of stitching in the new behaviour for us. All we have to do is derive from it. Problem solved.

The Problem with Message Maps

Well, not quite.

The problem would be solved if the MFC message map macros worked with template classes. Unfortunately they don't. In particular, BEGIN_MESSAGE_MAP just will not work with template classes. The reason is that this macro actually defines and initialises a couple of static members and functions. Syntactically, each of the definitions needs to be preceded by 'template ', but they are not, because the macro is written to work with nontemplate classes.

Here are the definitions for the message map macros:

#ifdef _AFXDLL#define DECLARE_MESSAGE_MAP() private: static const AFX_MSGMAP_ENTRY _messageEntries[]; protected: static AFX_DATA const AFX_MSGMAP messageMap; static const AFX_MSGMAP* PASCAL _GetBaseMessageMap(); virtual const AFX_MSGMAP* GetMessageMap() const; #else#define DECLARE_MESSAGE_MAP() private: static const AFX_MSGMAP_ENTRY _messageEntries[]; protected: static AFX_DATA const AFX_MSGMAP messageMap; virtual const AFX_MSGMAP* GetMessageMap() const; #endif#ifdef _AFXDLL#define BEGIN_MESSAGE_MAP(theClass, baseClass) const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() { return &baseClass::messageMap; } const AFX_MSGMAP* theClass::GetMessageMap() const { return &theClass::messageMap; } AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = { &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = { #else#define BEGIN_MESSAGE_MAP(theClass, baseClass) const AFX_MSGMAP* theClass::GetMessageMap() const { return &theClass::messageMap; } AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = { &baseClass::messageMap, &theClass::_messageEntries[0] }; AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = { #endif#define END_MESSAGE_MAP() {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } }; 

Notice that the macros are slightly different depending on whether or not they are being used within a MFC Extension DLL.

The DECLARE_MESSAGE_MAP and END_MESSAGE_MAP are both fine as they are. DECLARE_MESSAGE_MAP is used within the class declaration itself, and so doesn't need 'template ' prefixed. END_MESSAGE_MAP is also fine since it just adds the last line of initialisation to the message map data.

So we only need to make a special version of BEGIN_MESSAGE_MAP macro that will include the required template syntax. This is pretty straightforward:

#ifdef _AFXDLL#define BEGIN_MESSAGE_MAP_FOR_TEMPLATE(theClass, baseClass) template <class baseClass> const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() { return &baseClass::messageMap; } template <class baseClass> const AFX_MSGMAP* theClass::GetMessageMap() const { return &theClass::messageMap; } template <class baseClass> AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = { &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; template <class baseClass> AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = { #else#define BEGIN_MESSAGE_MAP_FOR_TEMPLATE(theClass, baseClass) template <class baseClass> const AFX_MSGMAP* theClass::GetMessageMap() const { return &theClass::messageMap; } template <class baseClass> AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = { &baseClass::messageMap, &theClass::_messageEntries[0] }; template <class baseClass> AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = { #endif

That should do it  we can now use BEGIN_MESSAGE_MAP_FOR_TEMPLATE when we define the message map and all should work just fine.

The Bug!

That is, if it were not for an unexpected error message. Please note that I use VC6.0 SP3 and build with the error-level set to level 4; I don't let my code build with even warning messages.

When I first built sample code that used this special version of the message map macro I got the following message within the macro line:

warning C4211: nonstandard extension used : redefined extern to static

Hmmm... what's going on here? Well, for a start, because I am using a macro, I cannot see the exact line of the macro that is causing the problem. So, the first step in such a case is to expand the macro by hand and use that instead of the macro call.

OK. I did that.It is not too hard when you use find and replace in the text editor. So I tried compiling again to see if it was more obvious where the error is. This time I see that the error is in the definition of theClass::messageMap. That is, the following line from the original macro:

template <class baseClass> AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = { &baseClass::messageMap, &theClass::_messageEntries[0] }; 

Well, no matter how many times I look at that code, I just cannot see anything wrong with it.

Time for the next step: Try to reproduce the problem in a simpler context and cut it down until I get the simplest code possible that still exhibits the bug. This is pretty much a binary search process. I start with the existing code and remove what doesn't look like it would affect the bug, one at a time. After each step, I recompile and verify that either the bug is still there or has disappeared. If it is still there, I carry on, otherwise I need to reinstate the section of code I removed and either try something else or split the removal into smaller steps. Some of the things that I tried first include removing the AFX_COMDAT and AFX_DATADEF macros (they made no difference); I also changed from the more complicated AFX_MSGMAP structure to just using a simple 'int'. Still the bug persisted.

I won't bore you (any further) with all the intermediate steps and false trails, but instead show you the code that demonstrates when the error happens and when it doesn't.

#include "stdafx.h"template <class T> class X1 {/* virtual */ const int* f() const { return &i; }static const int i;};template <class T> const int X1<T>::i = 1;X1<double> x1;template <class T> class X2 {virtual const int* f() const { return &i; }static /* const */ int i;};template <class T> int X2<T>::i = 2;X2<double> x2;template <class T> class X3 {virtual const int* f() const { return &i; }static const int i;};template <class T> const int X3<T>::i = 3;X3<double> x3;class X4 {virtual const int* f() const { return &i; }static const int i;};const int X4::i = 3;X4 x4;int main(){return 0;}

Look at the three template classes: X1, X2, and X3. X1 and X2 are both similar to X3, except that X1 makes function f() nonvirtual and X2 makes the static data value non-const. X4 is a non-template version. X1, X2 and X4 all compile just fine. However, X3 gives the same error message:

D:\SOURCE\TEST\Test.cpp(21) : warning C4211: nonstandard extension used : redefined extern to staticD:\SOURCE\TEST \Test.cpp(11) : while compiling class-template static data member 'const int X3::i'

This error message seems to make no sense at all. It seems that the compiler gets confused with a combination of template classes, virtual function and static const data. In other words, it is a compiler bug!

Well, I always have mixed feelings about compiler bugs. I get some satisfaction from knowing that the programmers at Microsoft are just as human as the rest of us and I feel relieved that it wasn't something I did wrong after all (this time). On the other hand, I'm annoyed that I had to spend so much time tracking down an error that was someone else's fault. And I then thinkhow am I going to work around it?

Well, in this case there is a fairly simple solution. Although I cannot make the functions non-virtual function, I can get rid of the const-ness of the static data. To do this, I need to change the definition of my special version of BEGINE_MESSAGE_MAP as well as DECLARE_MESSAGE_MAP (where the static data is declared). While I'm at it, I'll also make an exact copy of the END_MESSAGE_MAP macro so the names are all consistent.

This is what I ended up with:

#ifndef _MESSAGEMAPSFORTEMPLATES_#define _MESSAGEMAPSFORTEMPLATES_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000#ifdef _AFXDLL#define DECLARE_MESSAGE_MAP_FOR_TEMPLATE() private: static /*const*/ AFX_MSGMAP_ENTRY _messageEntries[]; protected: static AFX_DATA /*const*/ AFX_MSGMAP messageMap; static const AFX_MSGMAP* PASCAL _GetBaseMessageMap(); virtual const AFX_MSGMAP* GetMessageMap() const; #else#define DECLARE_MESSAGE_MAP_FOR_TEMPLATE() private: static /*const*/ AFX_MSGMAP_ENTRY _messageEntries[]; protected: static AFX_DATA /*const*/ AFX_MSGMAP messageMap; virtual const AFX_MSGMAP* GetMessageMap() const; #endif#ifdef _AFXDLL#define BEGIN_MESSAGE_MAP_FOR_TEMPLATE(theClass, baseClass) template <class baseClass> const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() { return &baseClass::messageMap; } template <class baseClass> const AFX_MSGMAP* theClass::GetMessageMap() const { return &theClass::messageMap; } template <class baseClass> AFX_COMDAT AFX_DATADEF /*const*/ AFX_MSGMAP theClass::messageMap = { &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; template <class baseClass> AFX_COMDAT /*const*/ AFX_MSGMAP_ENTRY theClass::_messageEntries[] = { #else#define BEGIN_MESSAGE_MAP_FOR_TEMPLATE(theClass, baseClass) template <class baseClass> const AFX_MSGMAP* theClass::GetMessageMap() const { return &theClass::messageMap; } template <class baseClass> AFX_COMDAT AFX_DATADEF /*const*/ AFX_MSGMAP theClass::messageMap = { &baseClass::messageMap, &theClass::_messageEntries[0] }; template <class baseClass> AFX_COMDAT /*const*/ AFX_MSGMAP_ENTRY theClass::_messageEntries[] = { #endif#define END_MESSAGE_MAP_FOR_TEMPLATE() END_MESSAGE_MAP()#endif

I can now use these macros as direct replacements for the original macros when I write a template class.

Phew!

In the next instalment of my series of articles, I'll combine the custom draw class from the last article with the techniques and bug work-arounds of this article to come up with a set of classes that helps with custom draw for common control.

Tools:
Add www.codeguru.com to your favorites
Add www.codeguru.com to your browser search box
IE 7
|
Firefox 2.0
|
Firefox 1.5.x
Receive news via our XML/RSS feed

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
MFC的消息映射機制揭秘
C宏——智者的利刃,愚者的惡夢(mèng)!
消息映射和命令傳遞(轉)
按工作方式不同,可將對話(huà)框分成兩類(lèi): ??模式對話(huà)框(modal dialog box模態(tài)對話(huà)框):在關(guān)閉模式對話(huà)框之前,程序不能進(jìn)行其他工作(如一般的“打開(kāi)文件”對話(huà)框) ??無(wú)模式對話(huà)框(mode
MFC消息響應機制及映射機制理解
duilib 消息宏分析
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久