Paul Dilascia
problem
My problem is about initializing C class members. I have seen a lot of such code (including it in your column):
CsomeClass :: csomeclass ()
{
X = 0;
Y = 1;
}
And where else is written as below:
CsomeClass :: csomeclass (): x (0), y (1)
{
}
Some of my programmers say that the second method is better, but they don't know why it is. Can you tell me the difference between these two membership initialization methods?
Reply
Technically, your programmer friend is right, but in most cases, the two actually have no difference. There are two reasons such that we choose a second syntax, which is called a member initialization list: one reason is necessary, the other is only considered for efficiency.
Let's take a look at the first reason - the need. Imagine that you have a class member, it itself is a class or structure, and there is only one constructor with a parameter.
Class cmember {
PUBLIC:
Cmember (int x) {...}
}
Because cmember has an explicit declared constructor, the compiler does not generate a default constructor (without parameters), so there is no integer that cannot create an instance of CMEMBER.
Cmember * pm = new cmember; // error !!
CMEMBER * PM = New cmember (2); // ok
If cmember is a member of another class, how do you initialize it? You must use a member to initialize a list.
Class CMYCLASS {
Cmember m_member;
PUBLIC:
CMYCLASS ();
}
/ / Must use the member initialization list
CMYCLASS :: CMYCLASS (): m_member (2)
{
•••••
}
There is no other way to pass the parameters to m_member, if the member is a constant object or reference is also. According to the rules of C , constant objects and references cannot be assigned, they can only be initialized.
The second reason is for efficiency, when the member class has a default constructor and an assignment operator. The MFC's CString provides a perfect example. Assume that you have a class CMYClass member M_STR, you want to initialize it into "Yada Yada.". You have two options:
CMYCLASS :: CMYCLASS () {
// Use assignment operators
// cstring :: Operator = (lpctstr);
m_str = _t ("Yada Yada");
}
// Use class members list
// and constructor cstring :: CString (lpctstr)
CMYCLASS :: CMYCLASS (): m_str (_t ("Yada Yada"))
{
}
Is there something difference between them? Yes. The compiler always ensures that all member objects are initialized before the constructor is executed, so the code compiled in the first example will call CString :: CString to initialize M_STR, which is completed before controlling the assignment statement. In the second example, the compiler generates a call to the CString :: CString (LPCTSTR) and passes "YADA YADA" to this function. The result is that two CSTRING functions (constructor and assignment operators) are called in the first example, and only one function is called in the second example. This doesn't matter in the case of CString, because the default constructor is inline, CSTRING is only assigned memory as a string when needed (ie, when you actually assign). However, in general, the repetitive function call is a waste of resources, especially when constructor and assignment operators allocate memory. In some big classes, you may have a constructor and an assignment operator to call the same init function responsible for allocating a large amount of memory space. In this case, you must use an initialization list to avoid not allocating two memory. Inside the type such as INTS or LONGS or other type without constructor, there is no performance difference in the initialization list and the two methods assigned in the constructor. Regardless of the way, only one assignment will occur. Some programmers say that you should always use the initial list to maintain a good habit, but I have never found that there is difficulty in converting the two methods. In the programming style, I tend to use assignments in the body, because more space is used to format and add a comment, you can write this statement: x = y = z = 0; or MEMSET (this, 0 SIZEOF (this));
Note that the second piece is definitely non-objective.
When I consider the problem of initialization list, there is a strange feature that I should warn you, it is a member of the C initialization class, which is initialized in the order of statements, rather than in the order in the initialization list.
Class CMYCLASS {
CMYCLASS (INT X, INT Y);
INT M_X;
INT M_Y;
}
CMYCLASS :: CMYCLASS (INT I): M_Y (i), m_x (m_y)
{
}
You may think that the above code will do M_Y = I, then do M_X = M_y, and finally they have the same value. However, the compiler first initializes M_x, then M_Y, because they are in this order. The result is that M_x will have an unpredictable value. My example is designed to illustrate this, but this BUG will appear more natural. There are two ways to avoid it, one is always in accordance with the order in which you want them to be initialized, the second is that if you decide to use the initialization list, you always follow these members in order to declare the order. This will help eliminate confusion.
problem
I just installed Windows® 2000 Release Candidate 1 on several machines. I don't know how to get the open dialog box with new Outlook style column in my MFC application (see Figure 1).
Figure 1 The New Open Dialog
Can I only set a logo, or if I need a new header file and a new public dialog DDL? I noticed that some old applications such as NotePad seem to get a new Open dialog without recompilation, but they are not MFC applications. Ideally, I want to get an application using the Old Dialog in Windows 9X and Windows NT®, and use the new dialog box in Windows 2000.
Warren Stevens
Reply
This problem is probably not to give you a happy answer. Windows 2000 new Open dialog is implemented with a new version of Commdlg.dll, which contains the "Places" section on the edge. Displaying its function is GetopenFileName, which is used in Windows 9x and Windows NT®. However, getopenFileName now uses a new version of OpenFileName, which is a structure that passes information between your applications and dialogs. New structure has some extra members: typedef struct tagofn {
DWORD lstructsize; // is very important!
•••••
// I'm thinking that you always know and like it.
#if (_WIN32_WINNT> = 0x0500)
Void * pvreserved;
DWORD DWRESERVED;
DWORD flagsex;
#ENDIF / / (_WIN32_WINNT> = 0x0500)
OpenFileName, * lpopenfilename;
Yes, this is the case. Windows 2000 is the 5th version of Windows, with a 16-en-blended representation is 0x500. If you use the _Win32_winnt = 0x0500 compiler, OpenFileName will get three new members. The first two are reserved, the third logo domain, Flagsex, has a new OFN_EX_NOPLACESBAR section, which blocks the Places column. Windows - or more accurately, Commmdlg.dll - Use OpenFileName first member lstructSize to decide to display that dialog, if lstructsize is 76 (old size), Windows runs the old dialog; if it is 76 3'4 = 88 (new size), it runs a new dialog - this is the friendly Redmondtonians initially told me. In my study, I found this is not a complete picture.
But before I detailed, let's take a look at MFC first, discuss another problem. In MFC applications, you don't often call GetopenFileName directly, but use cfiledialog - or, the framework uses CFiledialog. When the user calls File | Open, the control is controlled by CWINAPP: ONFILEOPEN and several other functions, and finally reaches CDOCManager :: DOPTFileName, this function creates a cfiledialog. CfileDialog has a data member with an OpenFileName structure:
Class CfileDialog: Public ccommondialog {
OpenFileName M_OFN;
•••••
}
The size of this structure is the size of OpenFileName when friendly Redmondtonians compiles MFC42.DLL; in other words, the old size. Moreover, if you are performing a static connection, the MFC code is frozen in the mfc42.dll or nafxcw.lib, you can't set only m_ofn.lstructSize as a new size, because CfileDialog has other data members in addition to M_OFN, they will Covered by a member of the new OpenFileName. No longer delay, I started using extreme methods to avoid this problem. I think what I can do, similar to the use of CPROPERTYPAGE in MFC. The size of PropSheetPage and PropSheetHead is increasing from Windows 95 to Windows 98, this is to support Wizard style pages. In order to support the newly expanded structure, the MFC provides CPROPERTYPAGEEX and CPROPERTYSHEETEX. The initial class (without EX) still uses the old structure; and the new class uses the new structure. This is a mixture, especially because AFXDLGS.H has its own old structure definition (AFX_ OldpropsheetPage and AFX_OLDPROPSHEETHEETHEETHEETHER, but thus doing. I have done the same thing for CfileDialog. First of all, I derive a new CFiledialoGex class, which has a new M_OFN that contains the new OpenFileNameEx structure, I imitate the 0x500 version to define. I join these three new members and use m_ofn. Rewrive the cfiledialog function. Unfortunately, because most of the MFC code is fixed, there is no virtual function, which means copying the original whole class. But I have been determined.
After I think I have found all the places in M_OFN, I rewrite it, I am very happy to compile my code (on Windows 98), then run - the result I got the old style dialogue frame. And there is a mystery. I forgot to consider: If Windows 2000 uses lstructSize to determine the Open dialog box, why get a new dialog box when running in Windows 2000? what! Notepad.exe appearing in Windows 98 is clearly in the size of the old OpenFileName on LSTRUCTSIZE, so Windows 2000 must use something other than LSTRUCTSIZE to decide to run the dialog.
Here, I decided to go back to the head to re-consider the problem. I put the MFC on one side and try to call GetopenFileName directly. I rewrive my app on the onfileopen:
Void cmyapp :: onfileopen ()
{
OpenFileName OFN; // Older Version
MEMSET (& OFN, 0, SIZEOF (OFN);
OFN.LSTRUCTSIZE = SizeOf (OFN);
INT NRESULT = :: GetopenFileName (& OFN);
}
Because running through this exercise, I use the old 0x400 version of the SDK file (because the application can run both in Windows 2000, or run on Windows 9x), OFN.LSTRUCTSIZE has the old size. When I compiled and run, I got the old dialog on Windows 98, and I got a new dialog on Windows 2000 - just like NOTEPAD! It can therefore be said, in fact, Windows 2000 is sufficiently savvy to use new dialogs for old applications - but not old MFC applications. It is meaningless. Where is the difference between an MFC application? It must be a sign. In order to find the truth, I have added different signs in the OpenFileName structure until my program generates an old style window without a Places column. You, when I join the flag OFN_ENABLEHOOK for the OFN.FLAGS, my dialog returns to the past. I report this strange behavior to Redmondtonians, confirmed that "this behavior is designed."
Then, Windows 2000 determines whether the size of the OpenFileName and whether the dialog uses the HOOK process. If OpenFileName has an old size, Windows 2000 uses OFN_ENABLEHOOK to determine which dialog runs. If OpenFileName uses the hook process (or sets orn_enableTemplate), Windows 2000 Displays dialog box in the old style; otherwise, the new dialog is displayed. This explains why the MFC app shows the old dialog - because CFiledialog, just like all MFC's public dialog box, use the hook process. This is how the MFC embeds the public dialog into its message mapping system, which uses AFXWndProc to embed other windows in the same way.
Now you have seen the results: you confused. The only way to get a new style dialog box in an MFC application is to completely bypass the CFiledialog, call GetopenFileName directly, and do not use the hook process. Even if you compile your app with the new SDK file and Winver = 0x500, you still can't use the MFC because it has an old size of the library and DLLs. You can compile the MFC using Winver = 0x500, but who knows how will it? And if you really build a new MFC, you will have to release a new DLL and your app, give it a different name, because your new MFC DLL will definitely not use CFiledialog with other hopes. Compatible with the old size of other structures. Or, you can regenerate the MFC, then static connection, which will greatly increase your executable size if you don't re-realize Windows.
To the time of the cut, I heard that in the upcoming new Visual C ®, there will be a new version of the MFC DLL of different names. The new version of MFC will support new UI and APIs that appear in Windows 2000.
At the same time, I will leave the picture 2 to you, it takes from the latest SDK documentation, "Header File Conventions", provides the key to the Windows version problem. It explains you that, in order to reach a version of Windows and Microsoft® Internet Explorer, you must use the SDK header file to define which macros. Don't fall on the expressway of life.
Paul Dilascia is Windows : Writing Reusable Windows Code In C (Addison-Wesley, 1992) author, or a free consultant and well-known bookmaker. His contact method: cppqa@microsoft.com or http://www.dilacia.com.