Managed C++ and .NET Development: Visual Studio .NET 2003 Edition
Creating a Managed C++ Class Library
Now that you have an empty solution, you'll put a project in it so you can start developing some code. The first project you're going to create is a class library called Cards to hold generic card and deck classes. Later you will use this library in your main DeckPlayer application.
The first thing you have to remember is that libraries are .NET assemblies. So are applications, for that matter. This being the case, you can create the assembly library using the method described in Chapter 3.
The first step in creating a library is the same no matter which method you use to create your library: You add a new project to the empty solution. You can do this in a number of ways, the easiest being to simply right-click the empty solution you created earlier in Solution Explorer and then select Add New Project from the drop-down menu. This brings up the Add New Project dialog box, which you saw when you created the empty solution (see Figure 6-16).
This time, to finish creating the empty solution, simply follow these steps:
-
Make sure that the Class Library (.NET) icon is selected in the Templates window.
-
Enter Cards as the name of the solution.
-
You don't need to change the location, as it should default to C:\Projects\Books\Managed C++\Source Code\Chapter06. This is the exact location I want to put the library.
Caution | If you used the Start Page to create a new project instead of the method described previously, make sure that the Add to Solution radio button is also selected. |
Unlike traditional class libraries, the class file automatically generated by the New Project dialog box will be used as a class linker and not a stand-alone class. If you recall from Chapter 3, the class linker does not need a header file, so you can just remove the header file that was autogenerated, as all it does is clutter up Solution Explorer. You will find Cards.h in the Header Files folder. To delete it, simply right-click Cards.h and then select Remove from the drop-down menu (see Figure 6-17).
Note | Removing a file from Solution Explorer does not physically delete the file from the solution's disk directory structure. |
Updating the Library Linker File
Because you have done all your design work beforehand (like you are supposed to), you know that the class library is made up of two classes: Card and Deck. This being the case, you can update Cards.cpp as shown in Listing 6-1.
Listing 6-1: Cards.cpp: The Class Library Linker File
// Cards.cpp the Class Linker file. #include "stdafx.h" using namespace System; #include "card.h" #include "deck.h"
For grins and giggles, add the following two to-do tasks to the Cards project:
-
Create Card Class
-
Create Deck Class
Okay, the tasks are obvious, but they demonstrate the process of adding tasks. First, click the "Click here to add new task" bar on the Task List view. Then type in the to-do task that needs to be completed. Optionally, you can add a priority to the task by left-clicking the first column (the one with the exclamation mark [!] header) of the Task List. This will bring up a small drop-down list from which you can select a low, normal, or high priority. Figure 6-18 shows what the Task List looks like after these two tasks have been added.
Adding a Managed C++ Class using Wizards
You have several approaches for adding classes to a class library assembly. Two of the methods that work best with the new method of building class library assemblies described in Chapter 3 are using a class-building wizard and adding the class manually.
The traditional method for creating a class is to generate both header and source files using a class-building wizard. In the new method of building class library assemblies, as you have seen, there is no need for the source (.cpp) file. Fortunately, Visual Studio .NET's class-building wizard has an option that also eliminates the need for the source (.cpp) file. The process is quite simple.
-
Right-click the project to which you want to add the class.
-
Select Add ➣ Add Class from the drop-down menu. This brings up the Add Class dialog box (see Figure 6-19).
Figure 6-19: The Add Class dialog box -
Navigate the Categories tree and open the Generic folder.
-
Select the Generic C++ Class icon from the Templates window.
-
Click Open. This brings up the Generic C++ Class Wizard dialog box (see Figure 6-20).
Figure 6-20: The Generic C++ Class Wizard dialog box -
Enter Card in the Class name text box.
-
Add C in front of the .h file text box. The reason you do this is that Microsoft has a default naming convention that places a C before a class. As this is the case, the .h file text box automatically strips this C. Because I don't follow this standard, I need to add the C back onto the .h file. By the way, the .cpp file also works the same way, but you're not going to create a .cpp file,
-
Put a check mark in the Inline box. This is important because if you don't check this option, the source (.cpp) file will be generated.
-
Leave the remaining controls as they are. Because you're inheriting from the default System::Object class, you don't need to fill in the Base class input field, but you can, if you want, type in System::Object. You want the class to have public access, so leave the Access drop-down box as it is. Finally, all .NET destructors are virtual, so you don't need to declare them as virtual. Therefore, you can safely ignore the Virtual destructor check box.
-
Click the Finish button.
You now have your first class (.h) file. The first thing you'll notice when you open the class file is there's no namespace created by the wizard. Because this is the case, you need to add a namespace manually. This is hardly difficult. You simply have to code it by hand in the main edit window. The second thing you'll notice is the new class that was generated isn't public or managed. Again, because the wizard didn't handle it, you need to code this yourself.
The Cards namespace also requires a public enum of card suits. There is no wizard to create an enum, either, so you have to add this manually.
Listing 6-2 shows what the class file should look like so far.
Listing 6-2: Initial Card.h File
#pragma once namespace Cards { public __value enum Suits { Heart, Diamond, Spade, Club }; public __gc class Card { public: Card(void) { } ~Card(void) { } }; }
Adding a Member Variable by Wizard
Now that you have the basic shell of the Card class completed, you need to add the member variables _type and _suit. The steps for creating these by wizard are virtually the same. The only differences are the variable type and the variable name. Here are the steps to create a member variable by wizard:
-
Open the Class View by selecting its tab from the View menu or by pressing Ctrl-Shift-C. I'm sure there are other ways to access the Class View as well.
-
Right-click the class to which you want to add the member variable—in this case, Card.
-
Select Add ➣ Add Variable from the drop-down menu. The Add Member Variable Wizard appears, as shown in Figure 6-21.
Figure 6-21: The Add Member Variable Wizard dialog box -
Select private from the Access drop-down list.
-
Enter a variable type of Int32 for _type or Suits for _suit.
-
Enter the appropriate variable name, either _type or _suit.
-
Click the Finish button.
After you have completed these steps for both variables, you should have two private member variables for the Card class.
Adding a Constructor, a Member Method, and Two Properties by Wizard
To finish off the Card class, you need to add a constructor, two properties, and a virtual function. The process for adding each of them is nearly the same. This makes sense, as they are, from a syntactical perspective, just different types of methods, the differences being that the constructor has no return type and the properties are prefixed by __property.
Here are the steps to create a method using Visual Studio .NET's wizards:
-
Open the Class View.
-
Right-click the class to which you want to add the member method—in this case, Card.
-
Select Add ➣ Add Function from the drop-down menu. The Add Member Function Wizard appears, as shown in Figure 6-22.
Figure 6-22: The Add Member Function Wizard dialog box -
Enter the return type of the member method. Remember, if the return type is a pointer, place the asterisk (*) after the return type. In the case of a constructor, leave this text box empty. For a property, you can only enter the return type. You get an error if you prefix the return type with __property in the return type edit box. Currently, you are going to have to add __property manually in the property declaration at the same time as you enter its implementation.
-
Enter the appropriate function name: Card, get_Type, get_Suit, or ToString.
-
For each parameter, enter its type and name, and then click the Add button.
-
Click the Finish button.
Now that you have all the definitions generated by the wizards, you need to add the actual implementations of each of the methods. Because there aren't many methods in this class, simply scrolling to the appropriate method is quite easy. On larger classes, it will probably be easier to navigate to the required method by double-clicking the method in the Class View or, if you don't have the Class View handy, selecting the method from the drop-down list at the top right of the main development window.
Listing 6-3 shows the complete class with all methods implemented. Notice that the default constructor and destructor were deleted because they are not needed.
Listing 6-3: Complete Card Class (.h) File
#pragma once namespace Cards { public __value enum Suits { Heart, Diamond, Spade, Club }; public __gc class Card { private: Int32 _type; Suits _suit; public: Card(Int32 type, Suits suit) { _type = type; _suit = suit; } __property Int32 get_Type() { return _type; } __property Suits get_Suit() { return _suit; } virtual String* ToString() { String *t; if (_type > 1 && _type < 11) t = _type.ToString(); else if (_type == 1) t = S"A"; else if (_type == 11) t = S"J"; else if (_type == 12) t = S"Q"; else t = S"K"; switch (_suit) { case Heart: return String::Concat(t, S"H"); case Diamond: return String::Concat(t, S"D"); case Spade: return String::Concat(t, S"S"); default: return String::Concat(t, S"C"); } } }; }
As you can see, even if you prefer to use wizards, you still need to perform several steps manually. It's almost easier to code the class without the help of the wizards. In fact, let's do that now.
Adding a Managed C++ Class Manually
As you now know, there's no need for a source (.cpp) file when you create a class library assembly. Thus, to add a new class to the library, you simply have to add a class (.h) file. The first step in the manual process still requires the use of a wizard (actually, it's more of a glorified dialog box) to create the class (.h) file and add it to Solution Explorer. To add the class (.h) file, follow these steps:
-
Right-click the Header Files folder in Solution Explorer. If you don't have Solution Explorer open, press Ctrl-Alt-L to make it the current active task window.
-
Select Add ➣ Add New Item from the drop-down menu. The Add New Item dialog box appears, as shown in Figure 6-23.
Figure 6-23: The Add New Item dialog box -
Navigate the Categories tree and open up the Visual C++ folder.
-
Select the Header File (.h) icon from the Templates window.
-
Enter the name of the class in the Name text box—in this case, Deck.
-
Verify the location where the class (.h) file will be placed. If the location is incorrect, change it.
-
Click Open. This creates an empty class (.h) file for you to start entering your code into.
-
Enter the code shown in Listing 6-4.
Listing 6-4: Complete Deck Class (.h) File
#pragma once namespace Cards { public __gc class Deck { Card *deck[]; Int32 curCard; public: Deck(void) { deck = new Card*[52]; for (Int32 i = 0; i < 13; i++) { deck[i] = new Card(i+1, Suits::Heart); deck[i+13] = new Card(i+1, Suits::Club); deck[i+26] = new Card(i+1, Suits::Diamond); deck[i+39] = new Card(i+1, Suits::Spade); } curCard = 0; } Card *Deal() { if (curCard < deck->Count) return deck[curCard++]; else return 0; } void Shuffle() { Random *r = new Random(); Card *tmp; Int32 j; for( int i = 0; i < deck->Count; i++ ) { j = r->Next(deck->Count); tmp = deck[j]; deck[j] = deck[i]; deck[i] = tmp; } curCard = 0; } }; }
Saving Templates in the Toolbox
Something that you'll notice as you code more and more classes is that they usually start with a common template (see Listing 6-5). If you're like me and you like to take every shortcut available, then you can save this template in the Toolbox so you don't have to type it again.
Listing 6-5: Class Template
#pragma once namespace XXX { public __gc class YYY public: YYY(void) { } }; }
Along with the drag-and-drop GUI design functionality, the Toolbox view provides a General tab for you to store things that you might want to use later. For example, you might want to save the class template shown in Listing 6-5. To move code to the Toolbox, simply select the code you want to move in the main development edit window and then drag it to the General tab in the Toolbox (see Figure 6-24). Once there, you can rename it by right-clicking the pasted item and selecting Rename from the drop-down menu. To copy the code out of the Toolbox, simply drag the entry you made in the Toolbox under the General tab to the main development edit window. You can also place the saved item on the edit window's current cursor location by double-clicking the item.
The Toolbox also stores all items that you have placed on the Clipboard for the current session in the Clipboard Ring tab. Be careful, though—these items disappear when you exit Visual Studio .NET. To make sure that the entry remains for your next session, you must copy it out of the Clipboard Ring tab and paste it into the General tab. You can do this by simply dragging and dropping the entry between the tabs.
Autoupdating the Class View
A neat feature of Visual Studio .NET is that when you edit your code in the main development window, the Class View and the drop-down method list at the top right of the edit window are updated as well. Figure 6-25 shows the Class View after the code for the Deck class has been entered.
Compiling a Project
Now that you have completed the code for the Cards class library, it is a simple matter to compile it into an assembly. Remember that all .NET libraries and applications compile into assemblies. Because you selected the Managed C++ Class Library project type previously, the project automatically configures the compile options to include /CLR. Therefore, to build the assembly, you simply have to select Build ➣ Build Solution from the main menu or press Ctrl-Shift-B.
Figure 6-26 shows the output of a successful compile. If you don't get this output, then make corrections to the errors that appear in your Task List and compile again.
After you have compiled your assembly successfully, you should have a file called Cards.dll in the directory Chapter06\cards\Debug. This file is your new library assembly.
Note | If you are in release configuration instead of the default debug configuration, then you will find the assembly in the directory Chapter06\cards\Release. |
Категории