Archive-name: C++-faq/part13
Posting-Frequency: monthly Last-modified: Jun 17, 2002 URL: http://www.parashift.com/c++-faq-lite/ See reader questions & answers on this topic! - Help others by sharing your knowledge AUTHOR: Marshall Cline / cline@parashift.com / 972-931-9470 COPYRIGHT: This posting is part of "C++ FAQ Lite." The entire "C++ FAQ Lite" document is Copyright(C)1991-2002 Marshall Cline, Ph.D., cline@parashift.com. All rights reserved. Copying is permitted only under designated situations. For details, see section [1]. NO WARRANTY: THIS WORK IS PROVIDED ON AN "AS IS" BASIS. THE AUTHOR PROVIDES NO WARRANTY WHATSOEVER, EITHER EXPRESS OR IMPLIED, REGARDING THE WORK, INCLUDING WARRANTIES WITH RESPECT TO ITS MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE. C++-FAQ-Lite != C++-FAQ-Book: This document, C++ FAQ Lite, is not the same as the C++ FAQ Book. The book (C++ FAQs, Cline and Lomow, Addison-Wesley) is 500% larger than this document, and is available in bookstores. For details, see section [3]. ============================================================================== SECTION [31]: How to mix C and C++ [31.1] What do I need to know when mixing C and C++ code? There are several caveats: * You must use your C++ compiler when compiling main() (e.g., for static initialization) * Your C++ compiler should direct the linking process (e.g., so it can get its special libraries) * Your C and C++ compilers probably need to come from same vendor and have compatible versions (e.g., so they have the same calling conventions) In addition, you'll need to read the rest of this section to find out how to make your C functions callable by C++ and/or your C++ functions callable by C. BTW there is another way to handle this whole thing: compile all your code (even your C-style code) using a C++ compiler. That pretty much eliminates the need to mix C and C++, plus it will cause you to be more careful (and possibly --hopefully!-- discover some bugs) in your C-style code. The down-side is that you'll need to update your C-style code in certain ways, basically because the C++ compiler is more careful/picky than your C compiler[6.10]. The point is that the effort required to clean up your C-style code may be less than the effort required to mix C and C++, and as a bonus you get cleaned up C-style code. Obviously you don't have much of a choice if you're not able to alter your C-style code (e.g., if it's from a third-party). ============================================================================== [31.2] How can I include a standard C header file in my C++ code? To #include a standard header file (such as <cstdio>), you don't have to do anything unusual. E.g., // This is C++ code #include <cstdio> // Nothing unusual in #include line int main() { std::printf("Hello world\n"); // Nothing unusual in the call either } If you think the std:: part of the std::printf() call is unusual, then the best thing to do is "get over it." In other words, it's the standard way to use names in the standard library, so you might as well start getting used to it now. However if you are compiling C code using your C++ compiler, you don't want to have to tweak all these calls from printf() to std::printf(). Fortunately in this case the C code will use the old-style header <stdio.h> rather than the new-style header <cstdio>, and the magic of namespaces will take care of everything else: /* This is C code that I'm compiling using a C++ compiler */ #include <stdio.h> /* Nothing unusual in #include line */ int main() { printf("Hello world\n"); /* Nothing unusual in the call either */ } Final comment: if you have C headers that are not part of the standard library, we have somewhat different guidelines for you. There are two cases: either you can't change the header[31.3], or you can change the header[31.4]. ============================================================================== [31.3] How can I include a non-system C header file in my C++ code? [UPDATED!] [Recently fixed a grammatical error in the first paragraph thanks to Randy Sherman (in 5/02).] If you are including a C header file that isn't provided by the system, you may need to wrap the #include line in an extern C { /*...*/ } construct. This tells the C++ compiler that the functions declared in the header file are C functions. // This is C++ code extern "C" { // Get declaration for f(int i, char c, float x) #include "my-C-code.h" } int main() { f(7, 'x', 3.14); // Note: nothing unusual in the call } Note: Somewhat different guidelines apply for C headers provided by the system (such as <cstdio>)[31.2] and for C headers that you can change[31.4]. ============================================================================== [31.4] How can I modify my own C header files so it's easier to #include them in C++ code? If you are including a C header file that isn't provided by the system, and if you are able to change the C header, you should strongly consider adding the extern C {...} logic inside the header to make it easier for C++ users to #include it into their C++ code. Since a C compiler won't understand the extern C construct, you must wrap the extern C { and } lines in an #ifdef so they won't be seen by normal C compilers. Step #1: Put the following lines at the very top of your C header file (note: the symbol __cplusplus is #defined if/only-if the compiler is a C++ compiler): #ifdef __cplusplus extern "C" { #endif Step #2: Put the following lines at the very bottom of your C header file: #ifdef __cplusplus } #endif Now you can #include your C header without any extern C nonsense in your C++ code: // This is C++ code // Get declaration for f(int i, char c, float x) #include "my-C-code.h" // Note: nothing unusual in #include line int main() { f(7, 'x', 3.14); // Note: nothing unusual in the call } Note: Somewhat different guidelines apply for C headers provided by the system (such as <cstdio>)[31.2] and for C headers that you can't change[31.3]. Note: #define macros are evil[6.14] in 4 different ways: evil#1[9.3], evil#2[36.2], evil#3[36.3], and evil#4[36.4]. But they're still useful sometimes. Just wash your hands after using them. ============================================================================== [31.5] How can I call a non-system C function f(int,char,float) from my C++ code? If you have an individual C function that you want to call, and for some reason you don't have or don't want to #include a C header file in which that function is declared, you can declare the individual C function in your C code using the extern C syntax. Naturally you need to use the full function prototype: extern "C" void f(int i, char c, float x); A block of several C functions can be grouped via braces: extern "C" { void f(int i, char c, float x); int g(char* s, const char* s2); double sqrtOfSumOfSquares(double a, double b); } After this you simply call the function just as if it was a C++ function: int main() { f(7, 'x', 3.14); // Note: nothing unusual in the call } ============================================================================== [31.6] How can I create a C++ function f(int,char,float) that is callable by my C code? The C++ compiler must know that f(int,char,float) is to be called by a C compiler using the extern C construct[31.3]: // This is C++ code // Declare f(int,char,float) using extern C: extern "C" void f(int i, char c, float x); // ... // Define f(int,char,float) in some C++ module: void f(int i, char c, float x) { // ... } The extern C line tells the compiler that the external information sent to the linker should use C calling conventions and name mangling (e.g., preceded by a single underscore). Since name overloading isn't supported by C, you can't make several overloaded functions simultaneously callable by a C program. ============================================================================== [31.7] Why is the linker giving errors for C/C++ functions being called from C++/C functions? If you didn't get your extern C right, you'll sometimes get linker errors rather than compiler errors. This is due to the fact that C++ compilers usually "mangle" function names (e.g., to support function overloading) differently than C compilers. See the previous two FAQs on how to use extern C. ============================================================================== [31.8] How can I pass an object of a C++ class to/from a C function? Here's an example (for info on extern C, see the previous two FAQs). Fred.h: /* This header can be read by both C and C++ compilers */ #ifndef FRED_H #define FRED_H #ifdef __cplusplus class Fred { public: Fred(); void wilma(int); private: int a_; }; #else typedef struct Fred Fred; #endif #ifdef __cplusplus extern "C" { #endif #if defined(__STDC__) || defined(__cplusplus) extern void c_function(Fred*); /* ANSI C prototypes */ extern Fred* cplusplus_callback_function(Fred*); #else extern void c_function(); /* K&R style */ extern Fred* cplusplus_callback_function(); #endif #ifdef __cplusplus } #endif #endif /*FRED_H*/ Fred.cpp: // This is C++ code #include "Fred.h" Fred::Fred() : a_(0) { } void Fred::wilma(int a) { } Fred* cplusplus_callback_function(Fred* fred) { fred->wilma(123); return fred; } main.cpp: // This is C++ code #include "Fred.h" int main() { Fred fred; c_function(&fred); return 0; } c-function.c: /* This is C code */ #include "Fred.h" void c_function(Fred* fred) { cplusplus_callback_function(fred); } Passing pointers to C++ objects to/from C functions will fail if you pass and get back something that isn't exactly the same pointer. For example, don't pass a base class pointer and receive back a derived class pointer, since your C compiler won't understand the pointer conversions necessary to handle multiple and/or virtual inheritance. ============================================================================== [31.9] Can my C function directly access data in an object of a C++ class? Sometimes. (For basic info on passing C++ objects to/from C functions, read the previous FAQ). You can safely access a C++ object's data from a C function if the C++ class: * Has no virtual[20] functions (including inherited virtual functions) * Has all its data in the same access-level section (private/protected/public) * Has no fully-contained subobjects with virtual[20] functions If the C++ class has any base classes at all (or if any fully contained subobjects have base classes), accessing the data will technically be non-portable, since class layout under inheritance isn't imposed by the language. However in practice, all C++ compilers do it the same way: the base class object appears first (in left-to-right order in the event of multiple inheritance), and member objects follow. Furthermore, if the class (or any base class) contains any virtual functions, almost all C++ compliers put a void* into the object either at the location of the first virtual function or at the very beginning of the object. Again, this is not required by the language, but it is the way "everyone" does it. If the class has any virtual base classes, it is even more complicated and less portable. One common implementation technique is for objects to contain an object of the virtual base class (V) last (regardless of where V shows up as a virtual base class in the inheritance hierarchy). The rest of the object's parts appear in the normal order. Every derived class that has V as a virtual base class actually has a pointer to the V part of the final object. ============================================================================== [31.10] Why do I feel like I'm "further from the machine" in C++ as opposed to C? Because you are. As an OO programming language, C++ allows you to model the problem domain itself, which allows you to program in the language of the problem domain rather than in the language of the solution domain. One of C's great strengths is the fact that it has "no hidden mechanism": what you see is what you get. You can read a C program and "see" every clock cycle. This is not the case in C++; old line C programmers (such as many of us once were) are often ambivalent (can you say, "hostile"?) about this feature. However after they've made the transition to OO thinking, they often realize that although C++ hides some mechanism from the programmer, it also provides a level of abstraction and economy of expression which lowers maintenance costs without destroying run-time performance. Naturally you can write bad code in any language; C++ doesn't guarantee any particular level of quality, reusability, abstraction, or any other measure of "goodness." C++ doesn't try to make it impossible for bad programmers to write bad programs; it enables reasonable developers to create superior software. ============================================================================== SECTION [32]: Pointers to member functions [32.1] Is the type of "pointer-to-member-function" different from "pointer-to-function"? Yep. Consider the following function: int f(char a, float b); The type of this function is different depending on whether it is an ordinary function or a non-static member function of some class: * Its type is "int (*)(char,float)" if an ordinary function * Its type is "int (Fred::*)(char,float)" if a non-static member function of class Fred Note: if it's a static member function of class Fred, its type is the same as if it was an ordinary function: "int (*)(char,float)". ============================================================================== [32.2] How do I pass a pointer to member function to a signal handler, X event callback, etc? Don't. Because a member function is meaningless without an object to invoke it on, you can't do this directly (if The X Windows System was rewritten in C++, it would probably pass references to objects around, not just pointers to functions; naturally the objects would embody the required function and probably a whole lot more). As a patch for existing software, use a top-level (non-member) function as a wrapper which takes an object obtained through some other technique (held in a global, perhaps). The top-level function would apply the desired member function against the global object. E.g., suppose you want to call Fred::memberFunction() on interrupt: class Fred { public: void memberFunction(); static void staticMemberFunction(); // A static member function can handle it // ... }; // Wrapper function uses a global to remember the object: Fred* object_which_will_handle_signal; void Fred_memberFunction_wrapper() { object_which_will_handle_signal->memberFunction(); } int main() { /* signal(SIGINT, Fred::memberFunction); */ // Can NOT do this signal(SIGINT, Fred_memberFunction_wrapper); // OK signal(SIGINT, Fred::staticMemberFunction); // Also OK } Note: static member functions do not require an actual object to be invoked, so pointers-to-static-member-functions are type compatible with regular pointers-to-functions. ============================================================================== [32.3] Why do I keep getting compile errors (type mismatch) when I try to use a member function as an interrupt service routine? This is a special case of the previous two questions, therefore read the previous two answers first. Non-static member functions have a hidden parameter that corresponds to the this pointer. The this pointer points to the instance data for the object. The interrupt hardware/firmware in the system is not capable of providing the this pointer argument. You must use "normal" functions (non class members) or static member functions as interrupt service routines. One possible solution is to use a static member as the interrupt service routine and have that function look somewhere to find the instance/member pair that should be called on interrupt. Thus the effect is that a member function is invoked on an interrupt, but for technical reasons you need to call an intermediate function first. ============================================================================== [32.4] Why am I having trouble taking the address of a C++ function? This is a corollary to the previous FAQ. Long answer: In C++, member functions have an implicit parameter which points to the object (the this pointer inside the member function). Normal C functions can be thought of as having a different calling convention from member functions, so the types of their pointers (pointer-to-member-function vs. pointer-to-function) are different and incompatible. C++ introduces a new type of pointer, called a pointer-to-member, which can be invoked only by providing an object. NOTE: do not attempt to "cast" a pointer-to-member-function into a pointer-to-function; the result is undefined and probably disastrous. E.g., a pointer-to-member-function is not required to contain the machine address of the appropriate function. As was said in the last example, if you have a pointer to a regular C function, use either a top-level (non-member) function, or a static (class) member function. ============================================================================== [32.5] How can I avoid syntax errors when calling a member function using a pointer-to-member-function? Two things: (1) use a typedef, and (2) use a #define macro. Here's the way you create the typedef: class Fred { public: int f(char x, float y); int g(char x, float y); int h(char x, float y); int i(char x, float y); // ... }; // FredMemberFn points to a member of Fred that takes (char,float) typedef int (Fred::*FredMemberFn)(char x, float y); Here's the way you create the #define macro (normally I dislike #define macros[9.3], but this is one of those rare cases where they actually improve the readability and writability of your code): #define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember)) Here's how you use these features: void userCode(Fred& fred, FredMemberFn memFn) { callMemberFunction(fred,memFn)('x', 3.14); // Would normally be: (fred.*memFn)('x', 3.14); } I strongly recommend these features. In the real world, member function invocations are a lot more complex than the simple example just given, and the difference in readability and writability is significant. comp.lang.c++ has had to endure hundreds and hundreds of postings from confused programmers who couldn't quite get the syntax right. Almost all these errors would have vanished had they used these features. Note: #define macros are evil[6.14] in 4 different ways: evil#1[9.3], evil#2[36.2], evil#3[36.3], and evil#4[36.4]. But they're still useful sometimes. But you should still feel a vague sense of shame after using them. ============================================================================== [32.6] How do I create and use an array of pointers to member functions? Use the usual typedef and #define macro[32.5] and you're 90% done. First, use a typedef: class Fred { public: int f(char x, float y); int g(char x, float y); int h(char x, float y); int i(char x, float y); // ... }; // FredMemberFn points to a member of Fred that takes (char,float) typedef int (Fred::*FredMemberFn)(char x, float y); That makes the array of pointers-to-member-functions straightforward: FredMemberFn a[] = { &Fred::f, &Fred::g, &Fred::h, &Fred::i }; Second, use the callMemberFunction macro: #define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember)) That makes calling one of the member functions on object "fred" straightforward: void userCode(Fred& fred, int memberFunctionNum) { // Assume memberFunctionNum is between 0 and 3 inclusive: callMemberFunction(fred, a[memberFunctionNum]) ('x', 3.14); } Note: #define macros are evil[6.14] in 4 different ways: evil#1[9.3], evil#2[36.2], evil#3[36.3], and evil#4[36.4]. But they're still useful sometimes. Feel ashamed, feel guilty, but when an evil construct like a macro improves your software, use it[6.14]. ============================================================================== SECTION [33]: Container classes and templates [33.1] Why should I use container classes rather than simple arrays? Because arrays are evil[6.14]. Let's assume the best case scenario: you're an experienced C programmer, which almost by definition means you're pretty good at working with arrays. You know you can handle the complexity; you've done it for years. And you're smart -- the smartest on the team -- the smartest in the whole company. But even given all that, please read this entire FAQ and think very carefully about it before you go into "business as usual" mode. Fundamentally it boils down to this simple fact: C++ is not C. That means (this might be painful for you!!) you'll need to set aside some of your hard earned wisdom from your vast experience in C. The two languages simply are different. The "best" way to do something in C is not always the same as the "best" way to do it in C++. If you really want to program in C, please do yourself a favor and program in C. But if you want to be really good at C++, then learn the C++ ways of doing things. You may be a C guru, but if you're just learning C++, you're just learning C++ -- you're a newbie. (Ouch; I know that had to hurt. Sorry.) Here's what you need to realize about containers vs. arrays: 1. Container classes make programmers more productive. So if you insist on using arrays while those around are willing to use container classes, you'll probably be less productive than they are (even if you're smarter and more experienced than they are!). 2. Container classes let programmers write more robust code. So if you insist on using arrays while those around are willing to use container classes, your code will probably have more bugs than their code (even if you're smarter and more experienced). 3. And if you're so smart and so experienced that you can use arrays as fast and as safe as they can use container classes, someone else will probably end up maintaining your code and they'll probably introduce bugs. Or worse, you'll be the only one who can maintain your code so management will yank you from development and move you into a full-time maintenance role -- just what you always wanted! Here are some specific problems with arrays: 1. Subscripts don't get checked to see if they are out of bounds. (Note that some container classes, such as std::vector, have methods to access elements with or without bounds checking on subscripts.) 2. Arrays often require you to allocate memory from the heap (see below for examples), in which case you must manually make sure the allocation is eventually deleted (even when someone throws an exception). When you use container classes, this memory management is handled automatically, but when you use arrays, you have to manually write a bunch of code (and unfortunately that code is often subtle and tricky[17.5]) to deal with this. For example, in addition to writing the code that destroys all the objects and deletes the memory, arrays often also force you you to write an extra try block with a catch clause that destroys all the objects, deletes the memory, then re-throws the exception. This is a real pain in the neck, as shown here[16.15]. When using container classes, things are much easier[16.16]. 3. You can't insert an element into the middle of the array, or even add one at the end, unless you allocate the array via the heap, and even then you must allocate a new array and copy the elements. 4. Container classes give you the choice of passing them by reference or by value, but arrays do not give you that choice: they are always passed by reference. If you want to simulate pass-by-value with an array, you have to manually write code that explicitly copies the array's elements (possibly allocating from the heap), along with code to clean up the copy when you're done with it. All this is handled automatically for you if you use a container class. 5. If your function has a non-static local array (i.e., an "auto" array), you cannot return that array, whereas the same is not true for objects of container classes. Here are some things to think about when using containers: 1. Different C++ containers have different strengths and weaknesses, but for any given job there's usually one of them that is better -- clearer, safer, easier/cheaper to maintain, and often more efficient -- than an array. For instance, - You might consider a std::map instead of manually writing code for a lookup table. - A std::map might also be used for a sparse array or sparse matrix. - A std::vector is the most array-like of the standard container classes, but it also offers various extra features such as bounds checking via the at() member function, insertions/removals of elements, automatic memory management even if someone throws an exception, ability to be passed both by reference and by value, etc. - A std::string is almost always better than an array of char[17.5] (you can think of a std::string as a "container class" for the sake of this discussion). 2. Container classes aren't best for everything, and sometimes you may need to use arrays. But that should be very rare, and if/when it happens: - Please design your container class's public interface in such a way that the code that uses the container class is unaware of the fact that there is an array inside. - The goal is to "bury" the array inside a container class. In other words, make sure there is a very small number of lines of code that directly touch the array (just your own methods of your container class) so everyone else (the users of your container class) can write code that doesn't depend on there being an array inside your container class. To net this out, arrays really are evil[6.14]. You may not think so if you're new to C++. But after you write a big pile of code that uses arrays (especially if you make your code leak-proof and exception-safe), you'll learn -- the hard way. Or you'll learn the easy way by believing those who've already done things like that. The choice is yours. ============================================================================== [33.2] How can I make a perl-like associative array in C++? Use the standard class template std::map<Key,Val>: #include <string> #include <map> #include <iostream> int main() { // age is a map from string to int std::map<std::string, int, std::less<std::string> > age; age["Fred"] = 42; // Fred is 42 years old age["Barney"] = 37; // Barney is 37 if (todayIsFredsBirthday()) // On Fred's birthday, ++ age["Fred"]; // increment Fred's age std::cout << "Fred is " << age["Fred"] << " years old\n"; } ============================================================================== [33.3] How can I build a <favorite container> of objects of different types? You can't, but you can fake it pretty well. In C/C++ all arrays are homogeneous (i.e., the elements are all the same type). However, with an extra layer of indirection you can give the appearance of a heterogeneous container (a heterogeneous container is a container where the contained objects are of different types). There are two cases with heterogeneous containers. The first case occurs when all objects you want to store in a container are publicly derived from a common base class. You can then declare/define your container to hold pointers to the base class. You indirectly store a derived class object in a container by storing the object's address as an element in the container. You can then access objects in the container indirectly through the pointers (enjoying polymorphic behavior). If you need to know the exact type of the object in the container you can use dynamic_cast<> or typeid(). You'll probably need the Virtual Constructor Idiom[20.6] to copy a container of disparate object types. The downside of this approach is that it makes memory management a little more problematic (who "owns" the pointed-to objects? if you delete these pointed-to objects when you destroy the container, how can you guarantee that no one else has a copy of one of these pointers? if you don't delete these pointed-to objects when you destroy the container, how can you be sure that someone else will eventually do the deleteing?). It also makes copying the container more complex (may actually break the container's copying functions since you don't want to copy the pointers, at least not when the container "owns" the pointed-to objects). The second case occurs when the object types are disjoint -- they do not share a common base class. The approach here is to use a handle class. The container is a container of handle objects (by value or by pointer, your choice; by value is easier). Each handle object knows how to "hold on to" (i.e. ,maintain a pointer to) one of the objects you want to put in the container. You can use either a single handle class with several different types of pointers as instance data, or a hierarchy of handle classes that shadow the various types you wish to contain (requires the container be of handle base class pointers). The downside of this approach is that it opens up the handle class(es) to maintenance every time you change the set of types that can be contained. The benefit is that you can use the handle class(es) to encapsulate most of the ugliness of memory management and object lifetime. Thus using handle objects may be beneficial even in the first case. ============================================================================== [33.4] How can I insert/access/change elements from a linked list/hashtable/etc? The most important thing to remember is this: don't roll your own from scratch unless there is a compelling reason to do so. In other words, instead of creating your own list or hashtable, use one of the standard class templates such as std::vector<T> or std::list<T> or whatever. Assuming you have a compelling reason to build your own container, here's how to handle inserting (or accessing, changing, etc.) the elements. To make the discussion concrete, I'll discuss how to insert an element into a linked list. This example is just complex enough that it generalizes pretty well to things like vectors, hash tables, binary trees, etc. A linked list makes it easy insert an element before the first or after the last element of the list, but limiting ourselves to these would produce a library that is too weak (a weak library is almost worse than no library). This answer will be a lot to swallow for novice C++'ers, so I'll give a couple of options. The first option is easiest; the second and third are better. 1. Empower the List with a "current location," and member functions such as advance(), backup(), atEnd(), atBegin(), getCurrElem(), setCurrElem(Elem), insertElem(Elem), and removeElem(). Although this works in small examples, the notion of a current position makes it difficult to access elements at two or more positions within the list (e.g., "for all pairs x,y do the following..."). 2. Remove the above member functions from List itself, and move them to a separate class, ListPosition. ListPosition would act as a "current position" within a list. This allows multiple positions within the same list. ListPosition would be a friend[14] of class List, so List can hide its innards from the outside world (else the innards of List would have to be publicized via public member functions in List). Note: ListPosition can use operator overloading for things like advance() and backup(), since operator overloading is syntactic sugar for normal member functions. 3. Consider the entire iteration as an atomic event, and create a class template that embodies this event. This enhances performance by allowing the public access member functions (which may be virtual[20] functions) to be avoided during the access, and this access often occurs within an inner loop. Unfortunately the class template will increase the size of your object code, since templates gain speed by duplicating code. For more, see [Koenig, "Templates as interfaces," JOOP, 4, 5 (Sept 91)], and [Stroustrup, "The C++ Programming Language Third Edition," under "Comparator"]. ============================================================================== [33.5] What's the idea behind templates? A template is a cookie-cutter that specifies how to cut cookies that all look pretty much the same (although the cookies can be made of various kinds of dough, they'll all have the same basic shape). In the same way, a class template is a cookie cutter for a description of how to build a family of classes that all look basically the same, and a function template describes how to build a family of similar looking functions. Class templates are often used to build type safe containers (although this only scratches the surface for how they can be used). ============================================================================== [33.6] What's the syntax / semantics for a "class template"? Consider a container class Array that acts like an array of integers: // This would go into a header file such as "Array.h" class Array { public: Array(int len=10) : len_(len), data_(new int[len]) { } ~Array() { delete[] data_; } int len() const { return len_; } const int& operator[](int i) const { return data_[check(i)]; } int& operator[](int i) { return data_[check(i)]; } Array(const Array&); Array& operator= (const Array&); private: int len_; int* data_; int check(int i) const { if (i < 0 || i >= len_) throw BoundsViol("Array", i, len_); return i; } }; Repeating the above over and over for Array of float, of char, of std::string, of Array-of-std::string, etc, will become tedious. // This would go into a header file such as "Array.h" template<class T> class Array { public: Array(int len=10) : len_(len), data_(new T[len]) { } ~Array() { delete[] data_; } int len() const { return len_; } const T& operator[](int i) const { return data_[check(i)]; } T& operator[](int i) { return data_[check(i)]; } Array(const Array<T>&); Array<T>& operator= (const Array<T>&); private: int len_; T* data_; int check(int i) const { if (i < 0 || i >= len_) throw BoundsViol("Array", i, len_); return i; } }; Unlike template functions[33.7], template classes (instantiations of class templates) need to be explicit about the parameters over which they are instantiating: int main() { Array<int> ai; Array<float> af; Array<char*> ac; Array<std::string> as; Array< Array<int> > aai; } Note the space between the two >'s in the last example. Without this space, the compiler would see a >> (right-shift) token instead of two >'s. ============================================================================== [33.7] What's the syntax / semantics for a "function template"? Consider this function that swaps its two integer arguments: void swap(int& x, int& y) { int tmp = x; x = y; y = tmp; } If we also had to swap floats, longs, Strings, Sets, and FileSystems, we'd get pretty tired of coding lines that look almost identical except for the type. Mindless repetition is an ideal job for a computer, hence a function template: template<class T> void swap(T& x, T& y) { T tmp = x; x = y; y = tmp; } Every time we used swap() with a given pair of types, the compiler will go to the above definition and will create yet another "template function" as an instantiation of the above. E.g., int main() { int i,j; /*...*/ swap(i,j); // Instantiates a swap for int float a,b; /*...*/ swap(a,b); // Instantiates a swap for float char c,d; /*...*/ swap(c,d); // Instantiates a swap for char std::string s,t; /*...*/ swap(s,t); // Instantiates a swap for std::string } Note: A "template function" is the instantiation of a "function template". ============================================================================== [33.8] How do I explicitly select which version of a function template should get called? When you call a function template, the compiler tries to deduce the template type. Most of the time it can do that successfully, but every once in a while you may want to help the compiler deduce the right type -- either because it cannot deduce the type at all, or perhaps because it would deduce the wrong type. For example, you might be calling a function template that doesn't have any parameters of its template argument types, or you might want to force the compiler to do certain promotions on the arguments before selecting the correct function template. In these cases you'll need to explicitly tell the compiler which instantiation of the function template should be called. Here is a sample function template where the template parameter T does not appear in the function's parameter list. In this case the compiler cannot deduce the template parameter types when the function is called. template<class T> void f() { // ... } To call this function with T being an int or a std::string, you could say: #include <string> void sample() { f<int>(); // type T will be int in this call f<std::string>(); // type T will be std::string in this call } Here is another function whose template parameters appear in the function's list of formal parameters (that is, the compiler can deduce the template type from the actual arguments): template<class T> void g(T x) { // ... } Now if you want to force the actual arguments to be promoted before the compiler deduces the template type, you can use the above technique. E.g., if you simply called g(42) you would get g<int>(42), but if you wanted to pass 42 to g<long>(), you could say this: g<long>(42). (Of course you could also promote the parameter explicitly, such as either g(long(42)) or even g(42L), but that ruins the example.) Similarly if you said g("xyz") you'd end up calling g<char*>(char*), but if you wanted to call the std::string version of g<>() you could say g<std::string>("xyz"). (Again you could also promote the argument, such as g(std::string("xyz")), but that's another story.) ============================================================================== [33.9] What is a "parameterized type"? Another way to say, "class templates." A parameterized type is a type that is parameterized over another type or some value. List<int> is a type (List) parameterized over another type (int). ============================================================================== [33.10] What is "genericity"? Yet another way to say, "class templates." Not to be confused with "generality" (which just means avoiding solutions which are overly specific), "genericity" means class templates. ============================================================================== [33.11] Why can't I separate the definition of my templates class from it's declaration and put it inside a .cpp file? In order to understand why things are the way they are, you must accept a few facts: 1. A template is not a class or a function. A template is a "pattern" that the compiler uses to generate a family of classes[33.6] or functions[33.7]. 2. In order for the compiler to generate the code, it must see both the template definition (not just declaration) and the specific types/whatever used to "fill in" the template. For example, if you're trying to use a Foo<int>, the compiler must see both the Foo template and the fact that you're trying to make a specific Foo<int>. 3. Your compiler probably doesn't remember the details of one .cpp file while it is compiling another .cpp file. It could, but most do not and if you are reading this FAQ, it almost definitely does not. BTW this is called the "separate compilation model." Now assuming you accept those facts, let's work an example to show why things are the way they are. Suppose you have a template Foo defined like this: template<class T> class Foo { public: Foo(); void someMethod(T x); private: T x; }; Along with similar definitions for the member functions: template<class T> Foo<T>::Foo() { ... } template<class T> void Foo<T>::someMethod(T x) { ... } Now suppose you have some code in file Bar.cpp that uses Foo<int>: // Bar.cpp void blah_blah_blah() { ... Foo<int> f; f.someMethod(5); ... } Clearly somebody somewhere is going to have to use the "pattern" for the constructor definition and for the someMethod() definition and instantiate those when T is actually int. But if you had put the definition of the constructor and someMethod() into file Foo.cpp, the compiler would see the template code when it compiled Foo.cpp and it would see Foo<int> when it compiled Bar.cpp, but there would never be a time when it saw both the template code and Foo<int>. So by rule #2 above, it could never generate the code for Foo<int>::someMethod(). The simplest approach to this is to add the definitions for all the methods (not just the inline methods) in Foo.hpp. There are other approaches as well, and some of these other approaches result in smaller executables at the expense of a little ease-of-use. A note to the experts: I have obviously made several simplifications above. This was intentional so please don't complain too loudly. For example, if you know enough to complain about my simplifications (e.g., if you know the difference between a .cpp file and a compilation unit, the difference between a class template and a template class, and the fact that templates really aren't just glorified macros), then you didn't need to read this answer in the first place -- this particular question/answer wasn't aimed at you. Bottom line: I simplified things so newbies would "get it," even if doing so offends some experts. ============================================================================== SECTION [34]: Class libraries [34.1] What is the "STL"? STL ("Standard Templates Library") is a library that consists mainly of (very efficient) container classes, along with some iterators and algorithms to work with the contents of these containers. Technically speaking the term "STL" is no longer meaningful since the classes provided by the STL have been fully integrated into the standard library, along with other standard classes like std::ostream, etc. Nonetheless many people still refer to the STL as if it was a separate thing, so you might as well get used to hearing that term. ============================================================================== [34.2] Where can I get a copy of "STL"? Since the classes that were part of the STL[34.1] have become part of the standard library, your compiler should provide these classes. If your compiler doesn't include these standard classes, either get an updated version of your compiler or download a copy of the STL classes from one of the following: * An STL site: ftp://ftp.cs.rpi.edu/pub/stl * STL HP official site: ftp://butler.hpl.hp.com/stl/ * Mirror site in Europe: www.maths.warwick.ac.uk/ftp/mirrors/c++/stl/ * STL code alternate: ftp://ftp.cs.rpi.edu/stl * The SGI implementation: www.sgi.com/Technology/STL/ * STLport: www.stlport.org STL hacks for GCC-2.6.3 are part of the GNU libg++ package 2.6.2.1 or later (and they may be in an earlier version as well). Thanks to Mike Lindner. Also you may as well get used to some people using "STL" to include the standard string header, "<string>", and others objecting to that usage. ============================================================================== [34.3] How can I find a Fred object in an STL container of Fred* such as std::vector<Fred*>? STL functions such as std::find_if() help you find a T element in a container of T's. But if you have a container of pointers such as std::vector<Fred*>, these functions will enable you to find an element that matches a given Fred* pointer, but they don't let you find an element that matches a given Fred object. The solution is to use an optional parameter that specifies the "match" function. The following class template lets you compare the objects on the other end of the dereferenced pointers. template<class T> class DereferencedEqual { public: DereferencedEqual(const T* p) : p_(p) { } bool operator() (const T* p2) const { return *p_ == *p2; } private: const T* p_; }; Now you can use this template to find an appropriate Fred object: void userCode(std::vector<Fred*> v, const Fred& match) { std::find_if(v.begin(), v.end(), DereferencedEqual<Fred>(&match)); // ... } ============================================================================== [34.4] Where can I get help on how to use STL? The STL FAQ: ftp://butler.hpl.hp.com/stl/stl.faq Kenny Zalewski's STL guide: www.cs.rpi.edu/projects/STL/htdocs/stl.html Mumit's STL Newbie's guide: www.xraylith.wisc.edu/~khan/software/stl/STL.newbie.html More info is in the book section[27.7]. ============================================================================== [34.5] How can you tell if you have a dynamically typed C++ class library? * Hint #1: when everything is derived from a single root class, usually Object. * Hint #2: when the container classes (List, Stack, Set, etc) are non-templates. * Hint #3: when the container classes (List, Stack, Set, etc) insert/extract elements as pointers to Object. This lets you put an Apple into such a container, but when you get it out, the compiler knows only that it is derived from Object, so you have to use a pointer cast to convert it back to an Apple*; and you'd better pray a lot that it really is an Apple, cause your blood is on your own head[26.10]). You can make the pointer cast "safe" by using dynamic_cast, but this dynamic testing is just that: dynamic. This coding style is the essence of dynamic typing in C++. You call a function that says "convert this Object into an Apple or give me NULL if its not an Apple," and you've got dynamic typing: you don't know what will happen until run-time. When you use templates to implement your containers, the C++ compiler can statically validate 90+% of an application's typing information (the figure "90+%" is apocryphal; some claim they always get 100%, those who need persistence[36.8] get something less than 100% static type checking). The point is: C++ gets genericity from templates, not from inheritance. ============================================================================== [34.6] What is the NIHCL? Where can I get it? NIHCL stands for "National-Institute-of-Health's-class-library." It can be acquired via ftp://128.231.128.7/pub/NIHCL/nihcl-3.0.tar.Z NIHCL (some people pronounce it "N-I-H-C-L," others pronounce it like "nickel") is a C++ translation of the Smalltalk class library[34.5]. There are some ways where NIHCL's use of dynamic typing helps (e.g., persistent[36.8] objects). There are also places where its use of dynamic typing creates tension[29.3] with the static typing of the C++ language. ============================================================================== [34.7] Where can I ftp the code that accompanies "Numerical Recipes"? This software is sold and therefore it would be illegal to provide it on the net. However, it's only about $30. ============================================================================== [34.8] Why is my executable so large? Many people are surprised by how big executables are, especially if the source code is trivial. For example, a simple "hello world" program can generate an executable that is larger than most people expect (40+K bytes). One reason executables can be large is that portions of the C++ runtime library gets linked with your program. How much gets linked in depends on how much of it you are using, and on how the implementer split up the library into pieces. For example, the <iostream> library is quite large, and consists of numerous classes and virtual[20] functions. Using any part of it might pull in nearly all of the <iostream> code as a result of the interdependencies. You might be able to make your program smaller by using a dynamically-linked version of the library instead of the static version. You have to consult your compiler manuals or the vendor's technical support for a more detailed answer. ============================================================================== [34.9] Where can I get tons and tons of more information on C++ class libraries? The C++ Libraries FAQ is maintained by Nikki Locke and is available at www.trumphurst.com/cpplibs1.html Also you should check out www.mathtools.net/C++/. They have a good pile of stuff organized into (at present) sixty-some categories. ============================================================================== User Contributions:Comment about this article, ask questions, or add new information about this topic: |
write my essay https://custompaperwritersservices.com cheap essay writer service https://essaywritingservicelinked.com
https://futureaitechnology.com/ai-chat-bot