Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2006-303
$eventId
ID of event: wwdc2006
$eventContentId
ID of session without event part: 303
$eventShortId
Shortened ID of event: wwdc06
$year
Year of session: 2006
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC06 • Session 303

"GCC, C++, and You"

Development Tools • 42:22

GCC is the powerful open-source compiler in Xcode that's used to build Mac OS X and thousands of third-party applications written in C, C++, Objective-C, and Objective-C++. Learn about the latest updates to GCC, and gain an in-depth understanding of advanced GCC C++ runtime behavior, including symbol hiding, overriding new and delete operators, and more.

Speaker: Geoff Keating

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Hi, I'm Geoffrey Keating, and I'll be giving this session number 303, GCC, C++, and You'. I'm an engineer, and I work on GCC at Apple. So, what you'll learn in this session, there are three basic things that you'll learn. So, first of all, I'll be telling you about how C++ works on Mac OS. I won't go so far as to say innermost details, but at least some of the details.

I'll tell you how to write your application so that you don't need to know how GCC works, which is probably what you're really after. And I'll be telling you about how to write a C++ library with a stable ABI so that you can do things like have release-to-release binary compatibility.

So what's different about C++? Why does this have to be a talk about C++ at all? So C++ has a bunch of features that aren't available in C. You can see the first four here are things that appear in other languages, like function overloading is in Java, exceptions you can find in Objective-C. Many languages have multiple namespaces that you can define things in.

There's also some things that are unique to C++. C++ has a very unique functionality for inlining, for example. C++ type linkage, and virtual functions, and multiple inheritance, and so on. All of these are things that you don't find in other languages. And all of these things require special runtime or ABI support. And many of them require consideration when you're designing your library, building an ABI, or just trying to work out what on earth is going on in your application.

So I'll start with the first two, namespaces and function overloading. So C++ has a bunch of ways where you can define the same function with the same name, as you can see here in the example, but the functions are all different. For example, you can define a function and then another function of the same name that's a member of a class. You can also use the C++ namespace feature to declare yet another copy of the same function.

C++ also has function overloading. You can define two functions of the same name in the same namespace, but if they take different parameter types, then they're still different. For example, here you see two functions which are different because they take different parameters. And finally, there are some functions in C++ that don't really have pronounceable or printable names at all. For example, here we have the dereference operator on a particular kind of parameter.

Now, the problem with all of these is that the Mac OS linker, and the linker on virtually every other operating system, expects that a function is given a particular name, and it can only be like a character string, preferably a reasonably readable character string, or at least one made up of ASCII characters. So you can't just tell the linker, I've got three things, and they're all named func, right? That's not going to work. It'll say, you've defined func three times. So instead, C++ gives these various functions mangled names.

For example, here, we have the three mangled names corresponding to these three functions. And you can see in each of these that the namespace is encoded into the mangled name, along with a bunch of other stuff, which looks totally unreadable. Likewise, when a function has different parameters, the parameters are also encoded into the mangled name. And finally, for functions that don't have printable names, they're given one. So here you can see it says ZDE, and actually DE stands for the reference operator.

So now, because the linker only knows about these mangled names, sometimes you need to know about it. The compiler, the debugger, and Shark will all mangle and de-mangle these names automatically. So you don't have to worry about how to mangle or de-mangle names, so long as you're using the compiler, the debugger, and Shark.

If you use the NM tool to see what the names are in a Mac OS object, You'll see that NM presents the names in the mangled form, because it tells you what the linker is thinking, and the linker only knows about mangled names. There is a tool called C++Field that'll take these mangled names and de-mangle them.

I should mention, though, that The demangling does not tell you everything about the mangled name. There are some fairly rare cases where two different demangled names will be mangled into the same form, or the other way around. If you ever look at assembly code, or worse, if you actually have to write assembly code to link with C++ objects, you'll find that that has to have mangled names, because the assembler also expects functions and so on to have a single name.

If you use export lists, then on Mac OS, export lists contain mangled names for maximum control. The disadvantage is that because they contain mangled names, it means you have to know what the mangled names are. If you want to find a mangled name, the easiest way to do it is to write a short C++ program that contains the mangled symbol that you're hoping for, run it through the compiler, and see what the compiler says the mangled name is. You can also go look at the multi-vendor C++ API and try to work it out yourself, But it's probably a lot easier if you just let the tool do it for you.

To avoid needing to deal with mangled names in an export list, the easiest thing to do is let the compiler know what the visibility is by using visibly attributes or pragmas instead, as in the code example here. And then you can see very simply that, and then you can simply add the attribute to your function and not have to worry what the mangled name of that function is.

So if you're trying to maintain a stable ABI, name mangling really is your friend. It's probably the best thing that you'll find has ever happened to you. Unlike in C, where if a function has a name, it's got that name, name mangling permits you to have many names for the same function. For example, suppose you've written your wonderful library.

So if you'd been doing this with an ABI compatibility, one thing you might consider is putting your great library inside a namespace if you're doing it in C++. This will make life easier for C++ users of your library because they don't have to worry about conflicts. But if you decide you need to maintain binary compatibility with this library, namespaces give you a wonderful advantage. For example, suppose your library, you've written a new version of your library. It's totally different. Nothing is the same. You've improved every little detail, or at least enough that everything is different.

What you can do is simply give it and put it in a new namespace. So instead of My Great Library, you might put it in My Great Library V2. This way, users will be able to use both versions of your library simultaneously, and you can have both versions of your library linked into a program at the same time. Everything will just work. The two libraries will be completely separate.

However, sometimes you need to be aware of name mangling when maintaining a stable ABI. For example, suppose you're converting your program to 64-bit, which is something you might want to consider doing around now. You might have found that originally you'd define some typedefs, where previously the typedef was int, but you've decided you'd really like it to be able to use a 64-bit value here on 64-bit systems.

Well, that's okay. Int and long are the same size, right? So you can just switch the typedef for everything, and it'll just work. Except, suppose the user has a function which takes one of these types as a parameter. In C++, typedefs don't really define a new type. They just give a new name to an old type.

So this function has changed from taking an int to taking a long, and that means the mangled names are going to change, because the mangled names include the parameters. So here you can see that an i in the mangled name has turned into an l. This will cause the function to be different, and means that if you're hoping for binary compatibility, it's not going to work anymore.

So you should avoid doing this in general. What you can do if you do need to change an int to a long is something that we've done a lot in the system headers. You make an int in 32-bit mode, and then you use the preprocessor to make it long in 64-bit mode. You can test, for example, the LP64 macro to do this.

Something else that you can do if you don't want to change the namespace of your entire library, but you've just got a small part of it that you've updated or changed, but the update makes the new type incompatible, is change the name of a structure type. For example, if you had my class, and you've decided to add a new field, or do something to it which would make it incompatible, you can change the name of that type. Then at least those two won't conflict, but because of name mangling, you get something else free.

If someone had a function which took that class as a parameter, they can now have two different functions, one for the old version and one for the new version. You can do this in your library, so that now the two functions don't conflict with each other, and you've managed to preserve your ABI because they have different names now.

One case where this doesn't work, unfortunately, is for return types. Because C++ doesn't do overloading on the return type, the return type isn't encoded in the function name. So in this case here, if you've gone from my class to an incompatible my class v2, these functions both still have the same name, because they have the same name and the same parameters. So now, unfortunately, doing this will break the ABI, and you'll need to work around any such cases, possibly using another of the techniques I've described. So that does us for namespaces and function overloadings. Now we're on to constructors and destructors.

A case where you particularly need to know about how C++ works with respect to constructors and destructors is if you're trying to maintain compatibility right the way back to Panther to 10.3.9. We made a change in Tiger, where instead of constructors running with the first time a function is used from your dynamic library, now they run at application startup.

This is definitely an improvement, because it means they now run at a predictable time instead of when the user just happens to use your library. But it's something you need to be aware of if you're porting code between the various versions of the operating system, that this has now changed.

Another thing you should be aware of about constructors is that with the new multi-core systems, you might be deciding to use threads more, or even add threads to a program that didn't have them. In 10.4 and later, constructors are thread-safe, so they get run at a predictable time, and in the rare cases where they have to get run when a particular routine is called, it's run in a thread-safe way to guarantee that the constructors run exactly once. In 10.3.9, this doesn't happen quite so reliably. So this is, again, something you need to be aware of if you're running code way back to 10.3.9.

For destructors of static variables, so for example, here we have a static variable named myVariable with a type which has a destructor associated with it. That's going to get run on program shutdown, basically when you call exit. Previously, they were run from the marco detour section. This is in Xcode versions 2.1 and previous, I believe.

In 2.2 and later, they all get registered to run with the add/exit functions. Most people will never notice this change of timing. However, sometimes it does cause problems, but we've produced a switch to enable you to go back to the old behavior. So if you're finding that your destructors are now running in a different order than they used to, and this is causing problems, you can simply use the no-use CXA at exit switch to go back to the old behavior.

Another case where destructors become interesting is with exceptions. So you'll know that in C++, if this routine here might throw, in this example, throws an exception, then the local variable called myVariable in this well-named example has a destructor, and so the destructor will get executed if my throw throws an exception. As well as, of course, the normal running of the destructor when the routine exits, if the routine doesn't get to throw an exception.

So I'll talk more about exception, about how this works later. But something to know about exception handling is that the Objective-C exception handling, which is new with Leopard, and the C++ exception handling use different underlying mechanisms. So if the routine that you're calling might throw an Objective-C exception instead of a C++ exception, you should know that the destructor won't get run. One way to deal with this is that whenever you transition between C++ code that might throw C++ exceptions and Objective-C code, Then what you're going to want to do is catch your C++ exception or Objective-C exception and re-throw it as the other kind.

So that, of course, takes us to exceptions. So here's a simple example of an exception. And you can see that there's some things that C++ does with exceptions that are somewhat complicated. C++ exception handling is based on types. You throw something of a particular type, and then you catch some things. Hopefully, one of the things you're catching matches that type. There's also an independent part.

So the language-specific exception handling lives in the C++ runtime. It determines which catch clauses actually catch which exceptions. There's also generic stack unwinding code, which can be used in it from any language. You can't catch exceptions that have a different language. So you can't catch C++ exceptions in C.

But you can unwind through a C stack frame. You can also use the cleanup attribute, the last bullet point here, to enable you to run basically a destructor from C. So if your C routine is calling a C++ routine, and might need to, for instance, deallocate memory or release a file descriptor, you can use a cleanup routine to do this.

If you're trying to maintain a stable ABI, which is a theme we'll come back to in this presentation many times, And you have a C++ shared library that throws exceptions. Then, first of all, you need to realize that the exceptions that you throw are as much a part of your ABI as the functions that you declare or the structures that you return. So it's important to keep this stable if you expect your callers to be catching these exceptions.

Something that helps with this is that if you have routines that don't throw exceptions at all, you can use throw clauses on them to declare this. This will help the user who might be reading your header, so it helps with the documentation. The compiler can occasionally warn you about this, about incompatibilities, if you're definitely throwing something from a routine that isn't supposed to throw at all. And this also helps a lot with code quality.

If the compiler sees that a routine can't throw an exception, it doesn't have to generate code to run the destructors, as you saw in the previous examples. So this can save a reasonable amount in code size. And you can see an example here of how to add a throw attribute to your declaration.

So that does this with exceptions. Now we'll move on to inlining. So you're probably thinking, well, every language has inlining, right? I mean, I can declare an inline function in C. And this is very true. However, C++ has one feature which makes its version of inlining really quite unique. For example, consider this function here. You'll notice this function declares a static variable called myVariable, which it then uses to count something or other.

A unique thing about C++ is that if this function is, say, in a header, maybe included into many different translation units, there are two things that are really neat. First of all, you don't have to tell the compiler one place where the variable is really defined. Secondly, all the addresses of the function are the same, too. So you can take the function's address, and it's everywhere.

But you haven't had to tell the compiler, "You've got to output one copy of this function here." So if you think about how this is implemented, it's kind of neat inside, because the compiler doesn't know that at any one point it has to have the one true copy of the function. And the same is true with the variable.

So there are a variety of ways that the system has dealt with this. On some systems, like on Windows and with GCC 3.3 on Mac OS, C++ is basically local to one dynamic library. So you get one copy of the static variable per dynamic library, unless you've somehow told the system to do something different. On Windows, you have export decorations that you can use to do this.

And we've kind of emulated that mode in GCC 4.0 with the visibility MS compat flag. But what we really think is the right way to do it, and it's done on Linux and on Mac OS X with GCC 4.0, is full C++ support across dynamic libraries. So you don't have to worry about explicitly telling the compiler to export anything, or telling the compiler where the one true copy should go. The system just deals with it for you.

So I'll tell you about how this works under the hood. Suppose you've got an application, and it's used the very inline function that we were talking about before. So it's used my variable, and it's done this in a header file. So what happens is this. GCC 4.0 will output a copy of the variable everywhere it thinks it might need one.

So in this example, you have an executable that's made up of two .o files. You get two copies of the variable, one in each .o file. Normally, if you tried to link an app like this, you'd get a link error. It would say, "My variable is declared twice." Instead, the compiler marks the variables as what we call coalesced.

And what this means to the linker is that one of these two copies of the variable should win. It should get into the final executable. One copy will be ignored, and all references to it will go to the copy that was actually used. So you'll end up with one copy of the variable in the executable. The same applies to the actual function itself. If an out-of-line copy was generated, you'll get one copy.

So that works for executables and that works on most operating systems. The static linker understands how to reduce these down to one copy. On Darwin, in addition, if you have a copy of the variable in a shared library, the same thing happens. So the loader, DYLD, will ignore the copy of the variable in the shared library and use only the copy in the executable.

So this has some important consequences. So first of all, if you've got slightly different copies of these inline functions running around, perhaps because you have a version 1 and a version 2, and you fixed a bug in version 2, that change won't take effect everywhere at once. In particular, if you've changed the copy in the shared library because you've written a new version, but you still have old executables running around, it's quite likely that the copy in the executable will be the one that's actually used.

So the change you made won't take effect everywhere at once. So you need to be aware of this and make sure that both versions will work together. Also, because this is done by name, right, by mangled name, static variables will still be shared if they have the same name and they're in the same function. So if you want them to be shared, make sure they continue to have the same name. And if you don't, of course, then you don't.

Also, even if you just recompile your program with a new version of the compiler, but you haven't changed any part of the sources. You'll find that out-of-line copies of inline functions might appear or go away as the compiler decides, you know, improves its optimization, improves its inlining heuristics.

So the presence or absence of such a copy of an inline function or any other coalesced symbol is not part of your library's ABI. So if you have tools, for example, that run through your shared library and use NM to check to see whether it's the same ABI as your previous version, you should make sure that these tools know not to worry about any coalesced symbol.

There's also a performance thing to worry about. So you might remember from that in C++, any method that is actually defined inside a class, like my method here, you can see it's got actual code there, or not very much, but some. All of those variables, all of those methods are actually automatically inline. So that means that this whole machinery gets invoked, right? It means that when your program starts up, the loader has to collapse all of these down into a single copy.

For just methods, if you're not comparing pointers to them, then you probably don't need any of this behavior to happen, and it does have a performance impact. So you can use the visibility inline's hidden flag to get that performance back by telling the loader that it doesn't need to worry about these functions. For inline functions which you've explicitly marked inline and aren't methods, you can do the same thing using the hidden attribute. This will improve your application startup time. Okay, so from inlining we're going to go to type linkage.

App linkage is something that works quite differently in C++ and C. In C, every time you declare a structure, it's a different structure. So if you define it for two different .o files, those structures are really, they're not the same type. This wouldn't be very useful, but C++ has a feature called type compatibility, where two structures that are different get matched up.

And if they have the same fields in the same order with the same names, and they have the same names, then they're considered to be compatible. And so you can do things like have a routine that returns them, and that's okay. This doesn't happen in C++. In C++, types never get matched up like this. Two types are either always the same or they're always different.

So, they're the same if they have the same name and they're in the same namespace and the hidden visibility is appropriately compatible. So this has some consequences. First of all, C++ says that two definitions of the same type have to be written the same, because unlike in C, where they can be matched up, it just requires you to make them the same.

Another place where this matters is that in C++ a bunch of things vary depending on whether two types are the same or not. For example, earlier I mentioned function overloading, and I said that you can have two functions with different parameter types, and then they become different. Well, so the way you tell if their types are different is by seeing if their name is different and so on. Likewise, when you specialise a template, the template that you specialise depends on what types are involved.

Earlier I mentioned exception handling. I said that when you catch an exception of a particular type, then the way a catch is matched to the actual exception that was thrown varies based on what type the actual exception was. And finally, you can actually compare types. You can simply get their type info and use the equals equals operator to see if two types are the same or not.

So all of this applies to classes and enums, and to fundamental types, and to pointers to them, and arrays of them, and that kind of thing. Type def names don't count. You remember I said earlier, a type def name is just a new name for an old type.

So this is actually a real thing, right, as opposed to just a language idea. There's a real data structure for each type called the type info. And two types are the same based on identity of the data structure. So the compiler and the runtime uses address equality to compare them. The two types are the same if their type info has the same address.

So in order to make this work, because you can declare a type in a shared library and then have the same type again in an application and so on, coalescing is used to combine all of these type info data structures down to just one copy for the one copy for the type.

So again, this has some consequences if you have shared libraries and you're worried about what your ABI for those libraries is. In C++, your types are part of your interface. So first of all, if you're using visibility attributes, you want to make sure that you mark your types, visible or hidden, according to whether you expected it to be visible or not. If you're using export lists, then you mark the type info entry as visible because the type info entry is what represents the type.

However, the compiler isn't guaranteed to generate a type info entry for every type that you just happened to mention. It'll only generate it if it needs it. So if it doesn't generate it, you want to make sure that you don't mention it in your export list. Or you can just use the pragmas or the attributes.

So that was types. So another thing that C++ has that I'm sure every C++ programmer knows is virtual functions. So a virtual function works like this. Normally, if you have a class and a subclass and they have a method, If you call the method, the method gets called based on what the compiler thinks the type is at the moment. However, if you have pointers and you want to make sure that the method that's called is the method of the most derived type, you can just mark them as virtual and the method will be called.

So as in this example here. Well, so how does this work underneath? Well, Sometimes the compiler knows, right? If you have a static variable of a particular type, the compiler can tell what the most derived type is, and it just knows which one, and it can work out which function to call directly.

When the underlying type is uncertain, like in the previous example where we were calling through a pointer, virtual function calls are made through procedure pointers. The procedure pointers are stored in a virtual function table, and each instance of the class, the first word of the instance is a pointer to the virtual function table.

If you make a subclass, the subclass's Vtable contains the Vtables of its parents. So in the diagram here you can see that in my subclass that contains the Vtable for my class, but there's still only one pointer, so you only pay four or eight bytes in every instance of your class to use virtual functions.

So, then you have the question of, well, where does this Vtable get output? So if the compiler can't work out any one place that it should be output, it just outputs it everywhere and uses coalescing as in the previous examples. But the compiler has an optimization to avoid having to do this all the time.

If there is a virtual method which is not declared as inline in the class and is not pure virtual, then the first of those virtual methods is called the key method. And the Vtable gets output in the same place as the key method is defined. And we know there's only one place because it wasn't inline, so you can only define it in one file in your program.

So, sometimes this causes a slightly less than optimal error message to appear. If you get a link error saying that the Vtable is undefined, then what that usually really means is not that the compiler has decided not to output it, but that the compiler was waiting to see the key method, and it never did. So usually that means that the key method is the thing that's really undefined.

So again, of course, virtual functions have an impact on the binary compatibility of your shared library. First of all, you'll remember in the previous diagram, that the subclass Vtable contains the Vtable for its parent. Well, so that means if you add something to its parent, it's not going to fit anymore.

So one thing you typically can't do to maintain ABI stability is adding to the list of virtual functions or deleting something from it or randomly changing it. By comparison, you can add to the list of non-virtual functions, and that's okay, because that's just like adding a new routine to your library.

If you're using an export list to control visibility, then you need to make sure that the Vtable symbols are visible if your class has a key method, because users of your library will be expecting to use that one copy of the Vtable. You'll also need to make sure that the functions mentioned in the Vtable, so any virtual functions in your library, are also visible. Because remember, a subclass has to create this new Vtable that contains your previous Vtable at the top.

So it's going to have to reference any virtual functions that were defined in your library. If you're using pragmas or attributes to control visibility, then by default the compiler will set everything up so that this works. You can still override the compiler if you're really sure that's what you want to do.

So that was virtual functions. We're still going to be talking about virtual functions for a bit, but now we're going to talk about the more complicated case of multiple inheritance, where a class has more than one parent. So in this example here you can see that my subclass has two parents, my class and my class two, and they both have a field in them.

Now, Now, normally if you're using single inheritance, life is very simple. A pointer to a subclass is also a pointer to the parent class, because the parent class goes at the top. If you have two parent classes, obviously this can't work. They can't both be on top. So what happens is they simply get queued up at the top of the class. But this means some things won't work quite so well. For example, suppose the second class, the one that didn't get to be on top, has a virtual method. It's expecting to be called with a pointer to a MyClass2.

But a pointer to the subclass is a pointer to a myClass, not a myClass2. So a small routine is created to adjust the pointer. It actually subtracts the appropriate amount from the pointer to adjust the pointer so that the virtual function gets the pointer that it's expecting. And there are a few other cases where similar little routines are generated. Now these routines are pretty small, they're only like four instructions or five instructions long, because all they have to do is adjust a pointer and then make a call. But unfortunately you still have to worry about them.

If you're using an export list, then you need to make sure that the thunk symbols are visible if the subclass is part of your interface. And you need to be aware that the mangled names of these functions encode the size of the offset that's added or subtracted, and that might vary between 64-bit and 32-bit, for example. So you'll want to have a different export list for each architecture. If you're using pragmas or attributes to control visibility, because you don't have to deal with mangled names, the compiler will deal with this for you.

The other thing that's different about C++, and a fairly substantial feature in it, is templates. So templates, to some degree, are not terribly interesting for ABI because they're just macros and they just expand. Unfortunately, C++ has this thing called implicit template instantiation, so you don't have to tell the compiler in advance what templates you're going to use. You can just decide to use them.

For example, here we've defined a variable called myVector, which is a vector of floats. And the system may not ever have heard of a vector of floats before. And this might not be the only place where it's mentioned. So templates are another place where coalescing is used to collapse all the various instantiations of the template down into just one.

Now, there are some things to be careful of here. For example, you should follow the C++ rules about templates from the standard library. And the rule I'm particularly thinking of is the one that says that you're not allowed to specialise it on standard types. For example, don't try to specialise a vector of float. The reason for this is that because coalescing works across the entire process space, This system might, as it turns out, be using a vector of floats.

If you specialize it and change the behavior, the system's going to get your changed behavior, which it probably wasn't expecting. Instead, what you can do is define a subclass. So you simply subclass a vector of float and override whatever routines you don't like. Another alternative is to simply include a vector of float in your class and then write and have your class be a wrapper around it.

So you can specialise vectors for on your own classes. You can also instantiate them of course on your own classes, otherwise they wouldn't be terribly useful. To avoid having conflicts, because as you can see in the example here, we've instantiated a vector of my class. Well, someone else in your application or someone else in the system might have decided that my class is a good class name too. In which case, the two my classes are going to be different. They would conflict. The vector instantiations will conflict. So to avoid this happening, it's good to keep your own classes in your own namespace.

If you have a shared library and it has a template in its header files and you need to change that to upgrade to the next version, you should remember that because coalescing is used, just like for inline functions, your clients may have their own instantiations of this, which will not automatically change just because you've changed your header file. They might need to recompile. So if you're making an incompatible change to a template, you need to place the updated templates in a different namespace.

One performance issue, which also helps with ABIs, is that you can prevent the compiler from using coalescing, generating a new copy of the template in every .o file and so on. You can use the external template functionality to tell the compiler, "I've already explicitly instantiated this template somewhere. Please don't make a new copy of it everywhere." This reduces your code size and dramatically reduces the size of your .o files. It also has ABI benefits because now you know where the copy of the template is. You can change it and know that it's been changed.

So I do strongly recommend using extern template in a shared library that is expected to be exporting templates. So another feature in C++ that is sort of available in C but not quite is that you can override the standard allocation and deallocation mechanisms, new and delete in C++. In C, this is like overriding mallocan, this is like replacing mallocan free.

So, I won't go into all the details of how to do it, but there is a code sample available on the WWDC website, and the C++ standard has extensive rules about what these new routines can and can't do. Please do follow the rules for a very important reason.

Because of inlining, it's not recommended that you try and override new and delete for just a single shared object, right, for a single application, and try and leave the rest of the system intact. Because what will happen is that parts of the system C++ library might be inlined into your application, they'll use your version, and other parts might not be inlined, and they won't. So you'll be having memory allocated with your version that's being trained to deallocate with the system version, or vice versa. And this is not likely to end up well.

So we recommend that you do just bite the bullet and overwrite new and delete for the whole system. But this does mean that the system will use your versions of new and delete. The system might even use it before all of your constructors have been run, for example.

No system library will ever override new or delete, so you know that your application can do it. And we recommend that you follow the same rule, that is that libraries that you write and give to others shouldn't override new or delete, unless they're supposed to be overriding new or delete, because that's what they're intended to do.

So that was kind of a lot of stuff. I have some rules here which, even if you forget the rest of the talk, might help. So the first thing to remember as a nice general rule is you should write an unusual app. Your app should be different and special in some way. But this doesn't mean that you have to write your app in an unusual way. What it does should be different and special. The way it's written, if at all possible, should be just like everybody else.

As much as is possible. Part of this is that if the system provides a functionality, it's really better to use that rather than inventing your own. For example, use vector of float rather than writing your own thing which contains a list of floats. The advantage of that is that we can improve the one in the system, maybe by adding vectorization to it, whereas we can't go into your application and change it.

You should know and be aware of the standards because the compiler specification is that it tries to compile standard C and standard C++. And it's good to read the documentation too. Often documentation has critical warnings about things. For example, much of what's in here is available somewhere in documentation.

Okay, and for more information, we have Matthew Formica, who is our Developer Tools Software Evangelist, and whose email address is printed in very small print on the screen. But you can also go to the WWDC website, which by now I hope you've all found, to find the sample code I mentioned earlier, important documentation about how the C++ ABI works, and all kinds of other neat stuff. and we're going to have Q&A in a few minutes, but if your questions don't get answered there, you can come to the Development Tools and Universal Binaries Lab in Lab B, which is right next to Lab A, that way at 3:30.