Documente online.
Zona de administrare documente. Fisierele tale
Am uitat parola x Creaza cont nou
 HomeExploreaza
upload
Upload




General-Purpose Classes

visual c en


General-Purpose Classes

MFC provides a very wide range of functionality, much of which you have already seen in the previous chapters. In this chapter, you will explore a hodgepodge of things that don't quite fit in the previous chapters, or were only mentioned in passing. In this chapter, you will see



  • How to use some of MFC's general-purpose classes
  • How to work with files in MFC
  • How to create your own collection classes
  • How to use MFC exception handling

CString

Traditional C-style, null-terminated strings and the wide array of functions used to work with them don't always fit very well into an object-oriented programming model. C-style strings also provide approximately one gazillion ways to shoot yourself in the foot. To support strings in C++, and to help save your feet, MFC provides the CString class, which also provides many functions and operators for simplified string handling.

The CString class can also be used independently of the rest of the MFC framework, because it is not derived from the CObject class. However, CString does define its own serialization operators (>>, <<) that are normally associated with CObject derivatives.

CString and C Strings

Although the CString class is intended to replace the old C-style strings, this does not mean that you have to abandon all the libraries you have developed to take character pointers for strings. MFC allows you to use the LPCTSTR operator to get at pointers to the null-terminated string contained in CString objects. Conversely, MFC also provides a CString constructor that accepts a character pointer, allowing you to substitute character pointers for CStrings in function calls.

NOTE

If you pass a CString to a variable argument function, such as printf(), make sure to explicitly cast it using LPCTSTR.

The one catch is that the LPCTSTR operator returns a const char*. This means that you will not be able to modify the string passed to a function with the LPCTSTR operator. If you need to pass a non-const char* pointer, you must use the GetBuffer() member of CString. This will return a non-const LPTSTR pointer, which will allow you to modify the data. GetBuffer() also takes a parameter to specify the minimum size of the buffer returned. This can be used to ensure that the buffer kept in the CString is sufficient for the data that you want to place in it.

If you use GetBuffer(), you must then call ReleaseBuffer() when you are finished with the pointer. This allows the CString to regroup and adjust its internal structures if necessary. Mostly, this means that the CString must figure out the new length of the string. You can pass the new length in the ReleaseBuffer() call, or you can pass -1 in ReleaseBuffer() to have the CString figure its own length. In this case, the buffer must be null-terminated, because the CString will simply call strlen() to get its new length.

CString and Unicode

In some cases, the characters in a CString aren't really chars at all. This is because the CString class is designed to support Unicode strings, which use a 16-bit data type to support character sets with more than 256 symbols.

If the symbol _UNICODE is defined in your application, the TCHAR type that CString works with is defined to be type wchar_t, which takes up 16 bits. All length functions then return values based on 1 character = 2 bytes.

If _UNICODE is not defined, TCHAR is defined as char. However, CString will still support multibyte character sets (MBCS), provided your application interprets MBCS lead and trail bytes on its own—all length computations are based on 8-bit characters.

You can define a Unicode string literal with the _T macro:

SetWindowText(_T'Hi Mom');

This will actually create an array of type TCHAR, which will be adjusted at compile-time to work with the appropriate type based on whether _UNICODE is defined. If there is any chance that your application will someday need to support Unicode, defining all your string literals this way is a good habit.

CString Memory Allocation Issues

When you pass a CString object to a function, you are actually passing a copy of the object by value, rather than just a pointer to it. If MFC actually made a copy of the whole array each time a CString was passed to a function, it is not hard to see that a performance problem might arise.

To remedy this, the copy constructor of the CString class will create a new CString that points to the same buffer as the original CString and increments a reference count for the original object. As the copied CStrings are destroyed, the reference count is decremented. The original object is destroyed only when the reference count reaches 0. In addition, a CString will allocate a new buffer and copy the original data before any modifying actions actually take place.

If you want to disable the reference counting mechanism for a CString, you can call LockBuffer() to set the reference count to -1. This will ensure that the CString object will always have its own buffer, which no other CString object will use, even when the CString is copied. You can undo this with UnlockBuffer().

You may free up any additional memory allocated by a CString that is not currently holding string data with FreeExtra().

Creating CString Objects

There are several ways to assign and retrieve the value of a CString. First, let's take a look at creating a CString and assigning it a value. The following example shows a few ways you can call the CString constructor:

CString strUnicode(_T('Unicode String'));
CString strAsciiZ('Non-Unicode String');
CString strCopy = strAsciiZ;
CString strExp = strUnicode + strAsciiZ;
CString strOne(`W');
CString strRepeat(`K', 9000);
CString strRes((LPCSTR)ID_FILE_NEW);
CString strNull(`0');
CString strEmpty;

Each of these lines represents a valid call to the CString constructor. Of particular interest is the constructor used for strRes, which will create a CString initialized with a value from a string resource—in this case ID_FILE_NEW. The resulting string will have the value Create a new document. You can also initialize a CString with a single character repeated a given number of times, as in the constructor for strRepeat, which will create a string of Ks even longer than Nolan Ryan's.

CString Expressions

The CString class overloads many common operators to work with CString objects. For starters, you can assign a value to a CString with the assignment operator (=). This will accept both CString and char* arguments:

CString strOne;
CString strTwo;
strOne = 'String One';
strTwo = strOne;

In addition, you can also use the plus sign (+) to concatenate two strings, provided one of the arguments is a CString, and += to add to a string, providing that the left operand is a CString:

CString strThree;
strThree = strOne + strTwo;
strThree = strOne + 'C style string';
strThree = 'C style string' + strTwo;
strThree = _T('Unicode string') + strTwo;
strThree += 'String Three';

Working with Individual Characters

You can access individual TCHAR elements in a CString with the GetAt() and SetAt() functions. In addition, you can use the [] operator in place of the GetAt() function. Unfortunately, you cannot also use this notation to modify characters in a CString. Note also that you cannot use SetAt() to add characters beyond the length of the CString or to insert the null character (`0'). The following shows a few ways these functions can be used:

CString strTmp = 'Hi Mom ';
TCHAR myChar;
myChar = strTmp.GetAt(0);
myChar = strTmp[0];
myChar.SetAt(6, `!');

Remember that syntax like this is not allowed:

myChar[6] = `!'; // Not Allowed

Comparing CString Values

The CString class provides several means of comparing strings that are both simpler to use and provide more functionality than strcmp(), although the Compare() function is quite similar, as shown in the following:

CString str1 = 'AAA';
CString str2 = 'BBB';
if(!str1.Compare(str2))
DoStringsEqual();
if(str1.Compare(str2) < 0)
DoStr1LessThanStr2();
if(str1.Compare(str2) > 0)
DoStr1GreaterThanStr2();

Like strcmp(), Compare() will perform a case-sensitive comparison. Using this example as an illustration, the Compare() function will return 0 if the strings are equal, < 0 if str1 is less than str2, and > 0 if str1 is greater than str2.

Using Compare() in many situations can be awkward or confusing. I'm not sure how many times I've had to look up the return values to be certain which string was actually greater. To simplify comparisons, the CString class implements the ==, !=, 949b16j >, <, >=, and <= operators. These will work as long as one of the operators is a CString, as shown in the following:

CString str1 = _T('AAA');
CString str2 = _T('BBB');
TCHAR* str3 = _T('CCC');
if((str1 == str2) ||
(str1 <= str3) ||
('DDD' >= str2))
DoWhatever();

In addition, you can use the CompareNoCase() function to do a case-insensitive comparison. This works just like the Compare() function otherwise.

Formatting a CString

The CString class also provides a member function to format a string that will be stored in the CString. This is the appropriately named Format() function. It accepts a format string and a variable argument list, just like the printf() family. In addition, in place of a format string, you may specify the resource ID of a string resource that contains the format string.

There is one thing to beware of with the Format() function, however. It works only with values that fit into a 128-byte buffer. If you need to Format() larger buffers, you should call GetBuffer() to allocate a larger buffer first:

CString strBig;
strBig.GetBuffer(1024);
strBig.Format('%d - %s', myInt, strSomeBigString);

Note that because you don't change the contents of the buffer that was returned by GetBuffer(), you don't need to call ReleaseBuffer() to reset the CString's size. You also need not worry about the size of a CString—it will support up to INT_MAX characters. If you need more than 2,147,483,647 characters, you'll just have to live without CString.

Other CString Functions

In addition to the functions already mentioned, the CString class supports many other functions. Most of the standard string functions that you are already familiar with are available in various CString member functions. These include functions to find substrings, trim whitespace, or convert case.

You will not find functions corresponding to functions such as sscanf() or strtok(). If you want to use these functions (or any others not implemented as member functions that may modify the string) you have to use GetBuffer() to get a pointer to work with, use your standard string functions on it, and then call ReleaseBuffer().

When Not to Use CStrings

The CString class is useful for simplifying many string operations and for supporting Unicode. Use of CString can also help prevent many common programming errors that are easily introduced in working with plain character arrays. However, there is a cost associated with this functionality. First, CString objects are passed by value. Second, temporary CString objects must often be created in CString expressions. Both of these can result in significant amounts of data moving around that don't really have to. For this reason, you should probably avoid CString types where this overhead will significantly affect the performance of your application.

Don't let this scare you away from the CString class; it is very appropriate for most general string applications, particularly as they relate to output presented to the user. Any performance difference that you might notice by using CString (you probably won't notice any) will easily be outweighed by the reduced development, debugging, and maintenance time for your applications.

You may want to combine the best of both worlds by storing your strings as CString, then getting the buffer with GetBuffer() to do your intensive processing before calling ReleaseBuffer() to return the buffer to the CString's control.

CTime and CTimeSpan

The MFC supplies the CTime and CTimeSpan classes to allow your applications to work with time values in your C++ applications. Between these two classes, most of the functionality of the ANSI time functions (those usually found in time.h) are implemented.

The CTime class encapsulates the time_t data type used in the C runtime libraries. This stores absolute times in seconds since January 1, 1970 in four bytes. This allows the representation of times from January 1, 1970 to January 18, 2038. If you need to represent a wider range of dates, see the section on the COleDateTime class later in this chapter.

Constructing a CTime Object

If you are planning to initialize a CTime to the current time, you will probably use the constructor with no parameters and then call GetCurrentTime() (more on this later). This constructor can also be used to create arrays of CTime objects.

You can construct a CTime based on another CTime or by passing a time_t structure, which CTime uses internally, to the constructor.

You may also initialize a CTime by passing the Win32 SYSTEMTIME or FILETIME structures, as well as the DOS time and date words. These constructors also take an optional parameter to indicate whether daylight savings time is in effect. By default, this parameter is -1, which tells MFC to automatically compute whether standard time or daylight savings time is in effect. Additionally, you may construct a CTime by specifying individual parameters for the year, month, day, hour, minute, and second.

CTime and Time Zones

Internally, the CTime class, and the time_t type it is based on, represent universal time coordinated (UTC) or Greenwich Mean Time (GMT). Most CTime functions, including the constructors, will perform the necessary conversions from UTC to local time, including daylight savings time, for you, based on the TZ environment variable. If this is not set when your program initializes, the runtime libraries will look to Windows for information about the time zone, as set in Control Panel.

GetCurrentTime()

You can assign the value of a CTime object to the current time by using the GetCurrentTime() function:

CTime tCurTime;
tCurTime = CTime::GetCurrentTime()

Note that this is a static function, requiring that you use the assignment operator rather than . or ->. You also must use CTime:: to qualify the function, distinguishing it from the Win32 API call ::GetCurrentTime().

Formatting the Time

You can create a formatted CString from a CTime object by using CTime::Format(). This function takes a pointer to a string specifying the format of the output with text and substitution variables specified with the percent sign (%). Any plain text in the format string will appear in the output string, and any of the substitution variables shown in Table 7.1 will be replaced with information from CTime.

Table 7.1. CTime format variables.

Format variable

Description

%a

Abbreviated day of week (Mon., Tue., and so forth)

%A

Full day of week (Monday, Tuesday, and so forth)

%b

Abbreviated month (Jan, Feb, and so forth)

%B

Full month (January, February, and so forth)

%c

Complete date and time for this locale (depends on settings in control panel)

%d

Day of month as decimal number

%H

Hour in 24-hour format (00 through 23)

%I

Hour in 12-hour format (01 through 12)

%j

Day of year (001 through 366)

%m

Month as decimal number (01 through 12)

%M

Minute as decimal number (00 through 59)

%p

AM/PM indicator, based on locale

%S

Second as decimal number (00 through 59)

%U

Week of year as decimal number (00 through 51); Sunday is first day of week

%w

Weekday as decimal number (0 through 6); Sunday is 0

%W

Week of year as decimal number (00 through 51); Monday is first day of week

%x

Date representation for current locale

%X

Time representation for current locale

%y

Year without century (96, 04, and so forth)

%Y

Year with century (1996, 2004, and so forth)

%z

Time zone name or abbreviation, lowercase (blank if time zone is unknown)

%Z

Time zone name or abbreviation, uppercase (blank if time zone is unknown)

Percentage sign

Extracting Time Values

You can extract individual components of the time from a CTime by using the GetYear(), GetMonth(), GetDay(), GetHour(), GetMinute(), GetSecond(), and GetDayOfWeek() functions. In addition, you can extract a time_t structure with GetTime(), or a tm structure with GetLocalTm() or GetGmtTm().

CTime Math

Several different operators are defined for use with CTime objects. First, you can use the assignment operator (=) to assign a value to a CTime from either another CTime or a time_t structure.

You may also compare two CTime objects with the ==, !=, <, >, <=, and >= operators.

You may also subtract a CTime from another CTime. This results in a CTimeSpan object, which MFC uses to represent relative times. You can then add or subtract a CTimeSpan from a CTime with +, -, +=, or -=.

CTimeSpan

You may construct a CTimeSpan object based on another CTimeSpan, a time_t structure, or explicit days, hours, minutes, and seconds specified in the constructor.

You may extract the components of a CTimeSpan with the GetDays(), GetHours(), GetTotalHours(), GetMinutes(), GetTotalMinutes(), GetSeconds(), and GetTotalSeconds() member functions. CTime also provides a Format() function very similar to CTime::Format().

CTimeSpan also supports the ==, !=, <, >, <=, and >= operators for comparing two CTimeSpan objects, as well as +, -, +=, and -= to add or subtract another CTimeSpan.

COleDateTime and COleDateTimeSpan

If your application needs to deal with a wider range of time values than that provided by the CTime class, COleDateTime may be just what you need. The COleDateTime class encapsulates the DATE type used by OLE. This type represents the time in seconds from midnight, December 30, 1899 to December 31, 9999. By convention, a time of 0:00 (midnight) is used in date-only values, and a date of 0 (December 30, 1899) is used in time-only values.

As you might guess from the name, COleDateTime is an OLE class, which will require your application to include afxdisp.h. This will include additional MFC components to support OLE and will make your application dependent on a few more system DLLs. If you are already using OLE, this doesn't add anything new, but if you are not already using OLE and are concerned about system resources, you may want to use your own alternative to the COleDateTime class.

The rest of the COleDateTime class, and its partner COleDateTimeSpan class, are very similar to the CTime and CTimeSpan classes, providing essentially the same operator and member function support.

The MFC File Classes

Eventually, your application will most likely need to work with files outside the document serialization discussed in the context of the document/view architecture. Here, you take a look at the various file classes provided by MFC to help with this.

The MFC file classes are all derived from CFile, which you can also use directly to provide unbuffered binary input and output services. You may also use the CFile member functions on any of the MFC file classes. The CInternetFile, CGopherFile, and CHttpFile classes, which you will learn about in Part V, 'Database Programming,' are derived from CStdioFile.

To support buffered stream files, MFC provides the CStdioFile class, which can deal with files in either binary or text mode. Text mode provides special processing for carriage return-linefeed pairs, whereas binary mode leaves the data as-is.

The MFC CMemFile class is used to support memory files, and the CSharedFile derived from it can be used to support shared memory files.

NOTE

The CSharedFile class may not be used to share memory between processes in Win32. To learn how to share memory between processes, see Chapter 8, 'Memory Management.'

MFC also provides the CSocketFile class, which you will explore in Chapter 16, 'Windows Sockets,' and the COleStreamFile class and a few derivatives. The latter are discussed in Part IV, 'Network Programming with Win32,' where you will take a closer look at OLE.

Opening a File

You can open a file by passing its pathname to the constructor for CFile. You must also specify a set of open flags, which is created by combining the following flags with the bitwise-OR operator (|):

CFile::modeCreate is used to create a new file. If you do not specify CFile::modeNoTruncate, the file will be truncated to 0 length.

CFile::modeRead, CFile::modeReadWrite, and CFile::modeWrite are used to specify the read/write mode for the file.

CFile::modeNoInherit prevents child processes from inheriting the file.

CFile::shareDenyNone, CFile::shareDenyRead, CFile::shareDenyWrite, and CFile::shareExclusive are used to open the file with a given share mode. shareDenyNone will allow other processes to access the file, and shareExclusive will not allow any other processes to access the file. shareDenyRead and shareDenyWrite disallow reading or writing by other processes. If the file has already been opened by a process that conflicts with these settings, creation will fail, throwing a CFileException.

In derived classes, such as CStdioFile, you may also specify CFile::typeText or CFile::typeBinary to enable or disable special processing for carriage return-linefeed pairs.

You must specify one access permission and one share option; the other flags are optional.

In addition, you may also construct an empty CFile object by constructing it with no parameters; then call CFile::Open() to open the file. Open() takes file path and flags parameters as the constructor does, but it also allows you to specify the exception that will be thrown if the open fails. More on file exceptions later in this chapter.

Reading and Writing with CFile

You can read or write a specified number of bytes by using CFile::Read() or CFile::Write(). Both of these begin at the current file position, which is set to the beginning of the file when it is opened.

To adjust the current position, you can use the SeekToBegin() or SeekToEnd() members, or the more general CFile::Seek() function. Seek() allows you to specify an offset for the new position, based on the second parameter. If CFile::begin is specified, the new position will be the specified number of bytes from the beginning of the file. If CFile::Current is specified, the current position will be moved the specified number of bytes forward from the current position. If CFile::end is specified, the current position will be set to the specified number of bytes from the end of the file. The offset may be negative, to allow seeking back into the file, or positive, to seek past the end of the file.

Reading and Writing with CStdioFile

For CStdioFiles, you may also use the CStdioFile::ReadLine() or CstdioFile::WriteLine() member functions, which will read or write an entire line at a time.

If you have specified CFile::typeText when the file was opened, these functions will provide some special processing for you. When writing, a newline character in the string to be written will be converted to a carriage return-linefeed pair in the file. When reading, carriage return-linefeed pairs are returned as a single newline character if you use the LPTSTR version of ReadString(). If you are using the version of ReadString() that returns a CString, the newline character is omitted.

CFile::Flush() can be called to ensure that all buffers are written to the file.

Getting Information About a File

Once you have opened a file, you can use CFile::GetStatus() to retrieve information about the file, including the time and date of its creation, last modification, and last read access, as well as its size and attributes.

MFC also provides a static version of GetStatus() that can be used on a file without opening it. This function will provide the information mentioned above, as well as filling in the complete pathname for the file.

The GetLength() and SetLength() functions can be used to manipulate the length of the file, and the GetPosition() function will return the value of the current position in the file.

You can also get additional information about the file from the GetFileName(), GetFileTitle(), and GetFilePath() functions.

You can also use several other static functions to work with files without opening them. SetStatus() can be used to set the file status, Rename() can be used to rename a file, and Remove() can be used to delete a file.

Closing Files

When you are finished with a file, you should call CFile::Close(). If you do not call Close(), it will be called automatically when the CFile object is destroyed. Note that deleting a CFile object does not delete the actual file.

CMemFile

The CMemFile class is used to create files in memory. The file is opened when you create it, so you will not need to call Open(). There are, however, some optional parameters to the CMemFile constructor that are of interest. The first version of the constructor allows you to specify the number of bytes that the file will allocate each time it needs to grow. This defaults to 1024 bytes.

You can then call Attach() whether you want to assign a particular buffer to the memory file.

Alternatively, you can assign a buffer to a memory file with a second version of the constructor, which takes a byte pointer to the buffer, its size, and the number of bytes to allocate when new memory is needed.

The Detach() function will effectively close the memory file and return a pointer to the data buffer with which your application can then work.

By default, the CMemFile class uses the malloc(), realloc(), memcpy(), and free() functions of the runtime library. If you want to use different routines, you may derive your own class from CMemFile, overriding the Alloc(), Free(), Realloc(), MemCpy(), and GrowFile() virtual member functions.

CSharedFile

The CSharedFile class is identical to the CMemFile class, other than the fact that it works with handles allocated with GlobalAlloc(), rather than pointers allocated with malloc(). This class does not allow you to share memory between processes in Win32. To learn how you can share memory between processes, see Chapter 8.

CArchive

The CArchive class is designed to store binary streams of data. This is most commonly used for object serialization, which is generally set up by the MFC framework. However, you may want to work more directly with the CArchive class in special serialization functions or in working with Windows Sockets.

When you create a CArchive object, you must pass a pointer to a CFile object and specify a mode for the archive, which will be either CArchive::load or CArchive::store. Each CArchive must be associated with a CFile, which you must create first. The access mode specified for the file must also be compatible with the mode selected for the archive. Furthermore, each file can have only one archive associated with it.

The CArchive class supports many operations that are very similar to CFile, such as Read(), Write(), WriteString(), and ReadString().

In addition, the CArchive class supports many other operations specific to the serialization process.

MFC Collections

It is very common for applications to keep collections of objects around. In C programs, this was often a simple array or a linked list of some sort. MFC provides classes for handling these structures in C++. Actually, MFC provides two sorts of collection classes: collection classes based on templates, which are more flexible but perhaps a bit more complicated, and the non- template original MFC collection classes released in MFC 1.0. Both sets of classes implement lists, arrays, and maps.

A list structure is just that—an ordered list of elements, providing for insertion of new elements at any point, as well as forward or backward traversal of the list.

An array structure is similar to standard C arrays, allowing the insertion and manipulation of elements based on an index. The MFC array classes are also able to grow dynamically as more space is required.

A map structure is a collection of objects that may be accessed with a key. This provides faster access to items by using a hashing technique to map keys to the corresponding values.

MFC NonTemplate Collections

In addition to the template list classes, which you will look at soon, MFC provides three predefined classes to implement doubly linked lists. CPtrList is provided to handle lists of void pointers, CStringList keeps a list of CString objects, and CObList stores a list of pointers to objects derived from CObject.

To support array classes, MFC provides many predefined array classes. CByteArray, CWordArray, CDWordArray, and CUIntArray keep arrays of the simple data types they are named for. In addition, the CPtrArray class keeps an array of null pointers, the CStringArray class maintains an array of CString objects, and the CObArray class works with an array of CObject pointers.

The classes that MFC supports for maps are named based on the mapping function they perform. For example, a map that stores pointer values based on a WORD key performs a WORD to

pointer mapping and is supported by the CMapWordToPtr class. MFC provides the following predefined map classes: CMapPtrToWord, CMapPtrToPtr, CMapStringToOb, CMapStringToPtr, CMapStringToString, CMapWordToOb, and CMapWordToPtr.

When using these classes, be aware that, for the most part, they are not type-safe. For instance, a CPtrList works with void pointers, allowing you to do just about whatever you like with them—including things you shouldn't do, such as trying to display a float as a string. To allow you to create truly type-safe collection classes, as well as to provide greater opportunities for customization, MFC has provided the template-based collection classes.

MFC Template-Based Collection Classes

The template classes that MFC provides allow you to create type-safe collections that contain objects of any type, including your own types. The MFC template-based collection classes come in two flavors: the simple array, list, and map classes; and arrays, lists, and maps of typed pointers.

The simple classes include the CArray, CList, and CMap template classes. These allow you to create collections of simple C++ types, C++ structures and classes, and other types that you define.

The pointer collection template classes include CTypedPtrArray, CTypedPtrList, and CTypedPtrMap. To use these classes, you will actually be deriving from one of the nontemplate collection classes. The template then provides the casting and type checking necessary to create a type-safe collection. If you are migrating from previous versions of MFC, using these classes is a step in the right direction. However, if you are creating applications from scratch, you should use the CArray, CList, and CMap template classes discussed here.

NOTE

To use these classes, you need to include AFXTEMPL.H in your application.

Declaring Simple Collection Classes

To declare a collection object based on the simple collection template classes, you need to specify the type of data you will be storing in the collection. Actually, you specify the type used for storing a value and the type used in function arguments. Generally, the argument type is a reference to the type stored in the collection, as you can see in the following declarations:

CArray<int, int> myArray;

CList<CPlayer, CPlayer&> myTeamList;

The first example declares an array of ints, where function arguments will pass int by value. The second example declares a list of CPlayer objects, where function arguments will use a reference to a CPlayer.

NOTE

The template classes make use of the copy constructors and assignment operators of your class. If you need to do anything special in these, make sure that these are properly defined.

Declaring map collections is similar, but takes four arguments, because you may have different types for the key and value data. Declaring a map looks something like this:

CMap< int, int, CPlayer, CPlayer& > myJerseyList;

This example declares a map that will map jersey numbers (ints) to CPlayer objects. Because the key values are ints, it is fine to pass them by value; however, you will want to use a reference to the more complicated CPlayer structure.

Working with Arrays

At this point, the three collection classes start to behave differently, so let's look at them separately. Note also that when I talk about arrays in this section, I am talking about objects derived from the CArray template class and not standard C++ arrays.

When an array is first declared, it is empty. If you know approximately how big your array will be, it is best to tell the array about your plans now so that it won't have to be reallocating memory as you add objects. This is done with the SetSize() member. You can then add objects in several different ways:

CArray<CPlayer, CPlayer&> myTeam;
myTeam.SetSize(10);
myTeam.Add(Player1);
myTeam[1] = Player2;
myTeam.SetAt(2, Player3);
myTeam.InsertAt(2, Player4);

This example creates a CArray of CPlayer objects. You then call SetSize() to pre-initialize the array, allocating most of the memory that you plan to use. The SetSize() function will also call the default constructor for CPlayer to initialize the values in the array.

You then add four items to the array, using the Add() member, with the [] and = operators, and with the SetAt() and InsertAt() functions. In the example, Player4 is inserted at index 2, resulting in Player3 being shifted to the next location.

Note that Add() will append the new entry to the end of the array. In this example, Player1 will be added as the eleventh object in the array (index 10).

Similarly, to access the data in your CArray, you can use the GetAt() function and the [] operator:

CString strName1 = myTeam[0].m_Name;
CString strName2 = myTeam.GetAt(1).m_Name;

The GetSize() and GetUpperBound() functions can be used to monitor the size or largest valid index of your array. These are essentially the same, but GetUpperBound() will always return one less, due to the fact that array indexes are zero-based.

When it's time to remove items from the array, you can use RemoveAt() to remove a specific entry. This shifts any elements beyond the removed element up. The RemoveAll() function can be used to remove all elements from the array. In addition, the FreeExtra() function can be used to tidy up by freeing any unused memory above the current upper-bound.

Template-Based Collection Class Helper Functions

The collection classes that you create with CArray, CList, and CMap use helper functions to perform many of the operations involving the objects in your collections. The helper functions used include ConstructElements(), DestructElements(), CopyElements(), CompareElements(), DumpElements(), SerializeElements(), and HashKey(). If the elements in your collections contain other classes, or if they manage their own memory, you will want to write your own implementations of the helper functions for the classes you will be using in collections.

Note that these helper functions are global functions and do not belong to any specific class. This also means that you need to implement them only once for each data type you will be using.

ConstructElements()

The ConstructElements() helper function is called whenever a template-based collection needs to add a new element to the collection. The default implementation simply allocates the required memory and initializes it with zeros. It does not call any constructors of objects.

The ConstructElements() function takes two parameters. The first is a pointer to the first element that needs to be initialized. The second is the number of elements to initialize. Here is a simple example of this:

void ConstructElements(CPlayer* pElement, int nCount)
// end ConstructElements

Although the syntax used to call the CPlayer constructor may seem a bit foreign, this will take the blank slate allocated by MFC and create a CPlayer on it.

If you are keeping a collection of pointers, MFC will allocate only the memory required for the pointer. Your ConstructElements() helper should then allocate a new object and initialize the pointer in the collection to point to the new object. If you were maintaining a collection of CPlayer pointers, the ConstructElements() function would look something like this:

void ConstructElements(CPlayer** pElement, int nCount)
// end ConstructElements
DestructElements()

The collection templates call DestructElements() whenever an element is destroyed. The default implementation does nothing. You should definitely provide your own implementation for this if you allocated your own memory in ConstructElements().

Like ConstructElements(), DestructElements() takes a pointer to the first element to be destroyed and a count of elements to destroy. For your CPlayer class, this would look something like this:

void DestructElements(CPlayer* pElement, int nCount)
// end DestructElements

Again, DestructElements() will be a little different if your collection contains pointers to objects, rather than the actual objects. This will look like the following:

void DestructElements(CPlayer** pElement, int nCount)
// end DestructElements
CopyElements()

The CopyElements() helper function is called by CArray::Append() and CArray::Copy() when MFC needs to copy a number of elements from one array to another. The default implementation uses memcpy() to move the raw data. If simply moving the individual bits of your class is not sufficient, you will need to implement CopyElements() for your class. The following example assumes that you have defined an assignment operator appropriate for your class:

void CopyElements(CPlayer* pDest, const CPlayer* pSrc, int nCount)
// end CopyElements

Once again, if your collection keeps pointers rather than the actual objects, you deal with the objects a bit differently.

CompareElements()

The CompareElements() helper function is called by CList::Find(), CMap::Lookup(), and CMap::operator []. If your collection will contain anything other than simple data types (int, char, and so forth), you either implement the comparison operator for your class or override the CompareElements() function to do the comparison. This function returns a BOOL, because the collections don't care about the relative values of two elements. For collections that keep complete objects in them, your override would look something like this:

BOOL CompareElements(const CPlayer* pElement1, const CPlayer* pElement2);
// end CompareElements

This example is trivial, provided you have already implemented the comparison operator for your class. If not, you need to figure out how to compare objects of your class and perform that here.

If you are using collections to handle pointers to your objects, you override this function, because the default will simply compare the two pointers, which will most likely not be equal, even if the objects they point to are.

DumpElements()

The DumpElements() function is called by the collection in debug builds whenever unreleased memory is detected. The default implementation does nothing, so overriding this simple function can prove very useful in debugging your helper functions.

The first parameter to DumpElements() is a reference to a dump context; the second and third parameters are the pointer to the first element to dump and the number of elements to dump.

Assuming you have already implemented a dump function for your class, it looks like this:

#ifdef _DEBUG
void DumpElements(const CDumpContext& dc, CPlayer* pElement, int nCount)
// end DumpElements
#endif
SerializeElements()

The collection templates will call SerializeElements() to serialize the objects in the collection. The default implementation simply does a bitwise copy. Once again, you see parameters for the first element and a count of elements, as well as a reference to the CArchive used for serialization. If your elements are derived from CObject, you can use the Serialize() function of your element class:

void SerializeElements(CArchive& ar, CPlayer* pElement, int nCount)
// end SerializeElements

If your element class is not derived from CObject, it is easiest to implement a Serialize() function similar to CObject's.

HashKey()

The HashKey() function is used only by the CMap template class, but because you are looking at helper functions, this seems a good place to discuss it. This function is used to generate a hash key to divide the keys to your collection into bins. If you desire a specific hash key scheme, you should override this function.

HashKey() takes one argument of the type you have specified for the key arguments and returns a UINT hash key value.

Working with CList

Like CArray collections, CList collections are born empty, although you can specify a block size parameter in the constructor. This is an int value that tells MFC to allocate memory in units of the specified number of entries.

Adding Elements

Next, you are likely to want to start adding items. You can do this with the AddHead() and AddTail() functions, which will add a new element to the head or tail of the list.

In addition, you may insert an element before or after a given position in the list, with the InsertBefore() or InsertAfter() member functions. These functions take a POSITION as the first parameter, as well as the element to be inserted.

The POSITION type is used by MFC's list and map collections to denote a particular spot in the collection. You can retrieve a valid POSITION in a list either by iteration through the elements of the list or by searching for a particular value.

Iteration

Applications will often find it necessary to traverse through the elements of a list. This process is known as iteration. To start a traversal of the list, you begin at either the start or end of the list. The GetHeadPosition() or GetTailPosition() functions will return a POSITION for the first or last elements.

To retrieve the first element, you can then pass the POSITION to the GetNext() function, which will return the element at the current position, and set the POSITION you passed to the POSITION value for the next element in the list. You can proceed in this manner until the last element in the list is retrieved, after which the POSITION variable will be set to NULL. To traverse the list backwards, the GetPrev() function works similarly.

To get or set an element at the current position, without changing the POSITION value, you can use the GetAt() or SetAt() functions.

Searching a List

The CList template class provides the FindIndex() function, which returns a POSITION value for an element of the list corresponding to the index you pass it. This is a zero-based index, much like you might use for normal arrays. In addition, you can use the Find() function to return a POSITION value for an element that is equivalent to the value passed to Find. You may also specify a POSITION to start searching after.

NOTE

Most documentation states that the Find() function compares only pointers. If your list contains pointer types, this is true. However, if you are including class objects in the list, the equivalency operator (==) defined for the class is used.

Removing Elements from a List

Before attempting to remove an element from a list, you should be certain that the list is not empty. You can test this with the IsEmpty() or GetCount() functions.

Then, if you need to remove items from a list, you can use the RemoveHead() or RemoveTail() functions to remove the first or last elements on the list, or the RemoveAt() function to remove an element at a specified POSITION. In addition, the RemoveAll() function will remove all elements from the list.

Working with Maps

The CMap collection template class allows you to create a collection of values that can be accessed directly by a corresponding key value. Remember that the declaration of a CMap object requires four parameters, rather than the two that are required for CArray or CList.

A map collection is also different from an array or list. You cannot just start throwing values into it with something like the Add() function. Instead, each value that you add to the map must be added with its key value. This is done with the SetAt() function. Note that the SetAt() function will replace an element already in the map if a matching key is found. The [] operator may be used to substitute for SetAt().

To retrieve an element from the map by its key value, you can use the Lookup() function, which takes a key value and a reference to the object retrieved. Lookup() returns TRUE if an element matching the key is found; otherwise, it returns FALSE. Although you can use the [] operator in place of SetAt(), you cannot use [] as a substitute for Lookup(), due to the possibility that a value may not be found.

In addition, you may traverse the map using a POSITION value. To get a starting POSITION value, call GetStartPosition(). You may then start retrieving elements with the GetNextAssoc() function, which moves the POSITION to the next element.

NOTE

Traversing the map using GetNextAssoc() does not return elements in any particular order. The order is indeterminate.

To remove an element based on its key, you can call the RemoveKey() function. Alternatively, you may call the RemoveAll() function to clear the entire map. The IsEmpty() and GetCount() functions are also provided to find the number of elements in the map.

Fine-Tuning Your Map

In many cases, you will want to be certain that the hashing used by your map collection is as efficient as possible so that the lookups and insertions are performed quickly. To optimize this, you should first consider implementing your own HashKey() helper function, described earlier. You can also use GetHashTableSize() to see the number of entries in the hash table, and InitHashTable() to initialize and set the number of entries in the hash table. This should be done before adding elements to the map collection.

Exception Handling

In many cases, it is very useful, if not absolutely necessary, to be able to provide exception handling to your application. This can also make your code much neater by providing a syntax that centralizes the code to handle common errors, avoiding error-prone nests of if blocks, based on function return codes. At the very least, you can help cut down on functions that use 101 different levels of indentation.

Current versions of the Visual C++ compiler (starting with 3.0) support the ANSI standard C++ exception handling mechanisms, which you see here. There are, however, several ways that Visual C++ can be used to support exception handling, which are provided only to support applications developed with previous versions of C and C++ compilers or previous versions of MFC.

These outdated methods include the older MFC-specific implementation, which used macros such as TRY and CATCH to implement exception handling, and the old C setjmp() and longjmp() functions. Although these methods are still supported in Visual C++ 5.0, you should definitely try to use the standard C++ exception handling mechanisms that you will look at here. In addition, you will see how to trap Win32 system-level exceptions.

In fact, MFC now implements the TRY-CATCH macro mechanism by using the ANSI try and catch keywords within the macros, so you might as well use the ANSI C++ syntax directly. This will also make your code much more portable, and it provides much better cleanup when an exception is thrown.

To enable the code to properly destroy objects left on the frame when an exception is thrown, you need to specify the /CX compiler switch or set Enable exception handling in your Project Settings | C/C++ | C++ Language page. If you do not specify this option, the compiler will warn you when it encounters a try block.

Using C++ Exception Handling

In C++, the try and catch keywords are used around a block of code for which you want to handle exceptions. In the catch statement, you can specify the sort of exception to handle. You may also specify several different catch blocks to handle different types of exceptions. The following example will help illustrate this:

try

catch( CMyException tmpExcept )

catch ( char* str )

catch ()

// Contine with your processing

If an exception is thrown within the try block, control will jump to the end of the try block and begin searching the catch blocks for a handler for the exception.

If a catch block is declared that matches the type of the exception thrown, the code in the catch block is executed, and execution resumes after the catch block.

If no catch block is found that matches the type of the exception, the application will unwind the call stack, searching for exception handlers at progressively higher levels until an appropriate handler is found.

If no exception is thrown, the catch blocks are not executed.

NOTE

The C++ exception handling model is nonresumable. Once an exception is thrown, control will not return to the try block.

The variable declared in the catch statement may be of any type that you may wish to throw. It may be a simple type, such as an int or char*, or any other type or class you define. However, MFC provides the CException class and several derivatives expressly designed to be used in throw and catch statements. In addition, you may specify as the exception type handled in a catch block, which will handle any type of exception. This may be used only in the last catch block for a given try block.

Throwing Exceptions

If at any point in your application you detect a condition that should cause your application to exit a try block and invoke an exception handler, you can throw your own exceptions with the throw statement. The throw statement uses syntax similar to that of the return statement, allowing you to throw any particular variable (or assignment-expression) that you like. Just remember that the type should match the type specified in a catch block somewhere.

NOTE

If you are throwing a pointer to any object, the object should be declared outside the try block, because objects declared inside the try block are destroyed before the handlers are called.

MFC Exception Classes

To simplify exception handling, MFC provides the CException class. The exceptions thrown by MFC classes are derived from the CException class. The MFC documentation will indicate the exceptions that may be thrown by a function, as in this example from CFile::Cfile():

CFile( LPCTSTR lpszFileName, UINT nOpenFlags );

throw( CFileException );

As in this example, most MFC functions do not actually throw a CException, but rather an exception class derived from it. Because the exceptions are derived from CException, you may handle any MFC exception by catching a CException pointer:

try

catch(CException* pMyException)

This example also shows the usage for the two member functions of CException. These functions may also be called for any of the exception classes discussed here.

GetErrorMessage() can be used to fill a buffer with an appropriate error message for the exception. ReportError() can be used to display the error message in a message box. ReportError() also allows you to specify any additional message-box styles and a resource ID for a string to be displayed if the exception does not provide one. By default, if the exception does not provide a message string, ReportError() will display No error message is available.

Note that you must handle a pointer to the exception class, because MFC will throw a pointer rather than the exception itself. If you want to throw one of the MFC exception classes, you will also need to throw the pointer, because the compiler won't find a copy constructor for CException.

CMemoryException

The CMemoryException is thrown in out-of-memory conditions. Specifically, this exception may be generated by the new operator when memory allocation fails. Because the MFC classes use the new operator, any MFC function that may allocate memory may also throw a CMemoryException.

If your application bypasses the new operator, by using malloc() or some other call to allocate memory, you should throw a pointer to a CMemoryException when the allocation fails. This is most easily done with ::AfxThrowMemoryException().

CNotSupportedException

MFC implements a few functions that are not supported. When you attempt to call unsupported functions, a CNotSupportedException is thrown. You may also find it handy to use this class in your own functions that, for one reason or another, are unsupported. You can throw an unsupported exception by calling ::AfxThrowUnsupportedException().

In general, CNotSupportedExceptions are thrown only when your application code is doing something it shouldn't. If you see these, you should probably look at just what your application is doing to generate them.

CFileException

The member functions of CFile, and its derived classes, may throw CFileExceptions when an error occurs. Additional information about the cause of the error is included in the CFileException class.

The m_cause member of CFileException holds a value representing the cause of the exception. The values for m_cause are defined in an enumerated type in CFileException and include the values shown in Table 7.2.

Table 7.2. CFileException::m_cause values

m_cause

Description

CFileException::none

No error occurred.

CFileException::generic

An unidentified error occurred; check m_lOsError.

CFileException::fileNotFound

File not found.

CFileException::badPath

All or part of a path is invalid.

CFileException::tooManyOpenFiles

Insufficient file open buffers or handles.

CFileException::accessDenied

The file permissions do not allow this access.

CFileException::invalidFile

Using an invalid handle was attempted.

CFileException::removeCurrentDir

Removing current directory disallowed.

CFileException::directoryFull

No more directory entries exist.

CFileException::badSeek

An error occurred trying to set the file pointer.

CFileException::hardIO

A hardware error occurred.

CFileException::sharingViolation

A shared region was locked or SHARE.EXE was not loaded.

CFileException::lockViolation

Locking attempted for an already locked region.

CFileException::diskFull

The disk is full.

CFileException::endOfFile

An attempt to read beyond end-of-file was made.

In addition to m_cause, CFileException provides the m_IOsError member that will reflect operating system-dependent error codes and the m_strFileName member, which contains the name of the file with which the exception was generated. The class also provides functions to map error codes to m_cause values. OsErrorToException() will return one of the enumerated types corresponding to an operating system error, such as the error codes defined in Stdlib.h. and ErrnoToException() will return a cause code from a runtime library error, as defined in Errno.h.

Throwing a CFileException

Although you could construct your own CFileException and throw a pointer to it, it is generally easier to use one of the two functions provided by CFileException to throw the exception for you.

ThrowOsError() will throw a CFileException with m_cause set to a value appropriate to the error. If the error code is unknown, m_cause will be set to CFileException::generic.

The ThrowErrno() function can be used to throw an exception based on your favorite error code from Errno.h. This will also initialize m_cause to an appropriate value.

These functions are both declared static, so you don't even need to bother with creating a CFileException object.

As with the other MFC exception classes, you may also call ::AfxThrowFileException() which allows you to specify the cause, OS error, and filename for the exception.

CArchiveException

CArchiveExceptions may be thrown by serialization operations gone bad. This class also includes the m_cause member, which will have one of the enumerated values shown in Table 7.3.

Table 7.3. CArchiveException m_cause values

m_cause

Description

CArchiveException::none

No error occurred.

CArchiveException::generic

Unidentified error.

CArchiveException::readOnly

Attempted to write to an archive opened for loading.

CArchiveException::endOfFile

End-of-file reached while reading an object.

CArchiveException::writeOnly

Attempted to read from an archive opened for storing.

CArchiveException::badIndex

Invalid file format.

CArchiveException::badClass

Attempted to read an object into an object of the wrong type.

CArchiveException::badSchema

Attempted to read an object with a different schema number.

If you find that you want to throw an archive exception in your application, you can call ::AfxThrowArchiveException() and specify one of the causes and an archive name to be included in the exception.

CResourceException

A CResourceException is generated when Windows has trouble finding or allocating a requested resource. These are most often thrown when trying to create objects that load resources such as dialog templates or bitmaps, or when trying to allocate GDI resources.

If your application needs to throw a CResourceException, you can call ::AfxThrowResourceException().

COleException

A COleException can be thrown by OLE operations that run into some sort of difficulty. This class includes the m_sc member that contains an OLE status code indicating the reason for the exception. You'll learn more about these in Part IV.

You may generate a COleException by calling ::AfxThrowOleException() and passing it a value for m_sc.

If you want to throw an exception from within an OLE automation function, you should look at ::AfxThrowOleDispatchException().

CUserException

The CUserException class is usually thrown in response to invalid or unacceptable user behavior. For example, MFC uses CUserException whenever data validation fails during dialog data exchange. This is a somewhat benign exception, due to the fact that MFC implements handlers for CUserException that do nothing other than exiting your try block.

If you wish to throw a CUserException, you should somehow notify the user of the problem (usually done with ::AfxMessageBox()) before calling ::AfxThrowUserException().

CDBException

A CDBException may be thrown by MFC's Open Database Connectivity (ODBC) objects (which is covered in Part V, 'Database Programming'). This class includes: the m_nRetCode member, containing an ODBC RETCODE return code; the m_strError member, containing a string describing the error; and the m_strStateNativeOrigin member, containing a formatted string that includes the SQLSTATE, the native error code, and information on the origin of the error. Your application may throw a CDBException by calling ::AfxThrowDBException().

CDaoException

The MFC classes that support database operations with Data Access Objects (DAO) may throw CDaoExceptions. You will take a closer look at the DAO classes and the errors they generate in Part V, so I'll keep it brief here.

CDaoException provides: the m_scode member, which holds an OLE SCODE value describing the error; the m_nAfxDaoError member, which gives information about a specific component that caused the exception; and the m_pErrorInfo member, which points to a CDaoErrorInfo object.

DAO exceptions can include more than one error, particularly if you are using an ODBC data source with DAO. To handle this, you can call GetErrorCount() to find the number of errors in the exception. You can then call GetErrorInfo() with the index of each error. This will update the m_pErrorInfo member to point at the CDaoErrorInfo object for that particular error.

You can throw a CDaoException with ::AfxThrowDaoException().

CInternetException

CInternetExceptions are thrown by the MFC Internet classes. You will look at the specifics of Internet exceptions in Chapter 22, 'The WinInet API,' so here's the condensed version: The CInternetException class includes the m_dwError member, containing an error value, including system error codes from Winerror.h and error values from Wininet.h. The m_dwContext member provides a context value associated with the internet operation that caused the exception.

There is not an Afx function to throw CInternetExceptions.

Win32 System Exceptions

For most of your exception handling needs, the standard C++ try and catch syntax, together with the MFC exception classes, should be more than adequate. However, if you need to trap system errors that occur at the system level, you use a slightly different mechanism.

As you will see in the following example, the syntax for handling Win32 system traps is similar to the C++ syntax, except try is replaced with __try and catch is replaced with __except:

float fResult;
__try

__except(GetExceptionCode() == EXCEPTION_FLT_DIVIDE_BY_ZERO)

The code in the __try block is executed like the try block in standard C++ until a system trap occurs. When an exception is raised, the except block is used to determine how to handle the exception.

This example uses the GetExceptionCode() function, which returns a value specifying the exception that occurred. These values are defined in Winbase.h and are in the following list (in addition, you include Winnt.h and Excpt.h to use this exception handling mechanism):

EXCEPTION_INT_DIVIDE_BY_ZERO
EXCEPTION_INT_OVERFLOW
EXCEPTION_FLT_DENORMAL_OPERAND
EXCEPTION_FLT_DIVIDE_BY_ZERO
EXCEPTION_FLT_INEXACT_RESULT
EXCEPTION_FLT_INVALID_OPERATION
EXCEPTION_FLT_OVERFLOW
EXCEPTION_FLT_UNDERFLOW
EXCEPTION_FLT_STACK_CHECK
EXCEPTION_ACCESS_VIOLATION
EXCEPTION_GUARD_PAGE
EXCEPTION_STACK_OVERFLOW
EXCEPTION_DATA_TYPE_MISALIGNMENT
EXCEPTION_ARRAY_BOUNDS_EXCEEDED
EXCEPTION_PRIV_INSTRUCTION
EXCEPTION_ILLEGAL_INSTRUCTION
EXCEPTION_INVALID_DISPOSITION
EXCEPTION_INVALID_HANDLE
EXCEPTION_IN_PAGE_ERROR
EXCEPTION_BREAKPOINT
EXCEPTION_SINGLE_STEP
EXCEPTION_NONCONTINUABLE_EXCEPTION

Actually, the expression used in the __except statement is a bit misleading. By definition, the expression in an __except statement should evaluate to one of three values: EXCEPTION_ EXECUTE_HANDLER (1),EXCEPTION_CONTINUE_SEARCH (0), or EXCEPTION_CONTINUE_EXECUTION (-1).

EXCEPTION_EXECUTE_HANDLER

Because the equivalence operator (==) returns 1 if its operands are equal, the expression used in the previous example evaluates to EXCEPTION_EXECUTE_HANDLER when the generated exception matches the exception you want to handle. In this case, the handler is executed, and the __try block is abandoned.

EXCEPTION_CONTINUE_SEARCH

If the expression evaluates to 0 (as when the operands of == are not equal), the search for an appropriate handler continues to the next __except block and on up the stack.

EXCEPTION_CONTINUE_EXECUTION

If the expression evaluates to -1, the code attempts to press on from the point of the exception. In some cases, the exception is fatal, and choosing EXCEPTION_CONTINUE_EXECUTION will result in a new trap of type EXCEPTION_NONCONTINUABLE_EXCEPTION.

The Catch

Visual C++ will not allow you to use Win32 System trap handling in the same function with standard C++ exception handling. If you need to mix the two, you use a separate function for trapping system traps, which can then be called from within your try block.

You should also be aware that the individual system traps will behave differently on the different hardware platforms supported by Windows NT.

Summary

In this chapter, you looked at some of the general-purpose classes provided by the Microsoft Foundation Classes, including the CFile class and its derivatives, which are used for file operations, and the MFC collection template classes, which can be used to work with common collections of objects. You have also learned how Visual C++ implements exception handling, including MFC's exception classes, and Win32 system traps.


Document Info


Accesari: 1175
Apreciat: hand-up

Comenteaza documentul:

Nu esti inregistrat
Trebuie sa fii utilizator inregistrat pentru a putea comenta


Creaza cont nou

A fost util?

Daca documentul a fost util si crezi ca merita
sa adaugi un link catre el la tine in site


in pagina web a site-ului tau.




eCoduri.com - coduri postale, contabile, CAEN sau bancare

Politica de confidentialitate | Termenii si conditii de utilizare




Copyright © Contact (SCRIGROUP Int. 2024 )