Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 67
Текст из файла (страница 67)
Instead, I’ve tried to focus on a few aspects thatcan improve your C++ code on Symbian OS. The chapter covers some ofthe main features of the Symbian OS coding standards and distils advicethat developers working on Symbian OS have found to be most useful.While it concentrates on issues specific to Symbian OS, it also has sometips for general good programming style. I used some excellent books onC++ for background information when researching this chapter; those Irecommend in particular are listed in the Bibliography.21.1 Reduce the Size of Program CodeSpace is at a premium on Symbian OS.
Where possible, you shouldattempt to minimize the size of your program binary and its use ofmemory without unduly complicating your code. In previous chapters,I’ve already discussed some of the factors that may increase the size ofyour object code. For example, Chapter 5 mentioned that the use of the_L macro is deprecated and you should prefer to use _LIT for literaldescriptors. The TRAP/TRAPD macros, which I discussed in Chapter 2,can also bloat program code.
The following discussion describes someother factors to consider which may help you trim the size of yourprogram binaries.318GOOD CODE STYLETake Care When Using inlineBeware of inlining functions, since you may end up bloating your programcode while you are trying to speed it up. You’ll find a lot more aboutthe hazards of inlining in Scott Meyers’ Effective C++.1 To summarize:the inline directive, when obeyed by a compiler, will replace a callto the inlined function with the body of the function, thus avoiding theoverhead of a function call.
However, this will increase the size of yourcode if the function code is anything other than trivial. (If the directiveis ignored things may be even worse, since the compiler may generatemultiple copies of your function code, while still making function callsto it, thus bloating your binary without any of the speed benefits fromforgoing the function call.) On Symbian OS, limited memory resourcestypically mean that the overhead of a function call is preferable to thepotential code bloat from a large section of inlined code.Furthermore, inlined code is impossible to upgrade without breakingbinary compatibility.
If a client uses your function, it is compiled into theircode, and any change you make to it later will force them to recompile,as I described in Chapter 18.For both reasons, stick to the rule that you should prefer a function calland return over an inlined function unless the function is trivial, such asa simple ”getter” type function, shown below:class TBook{public:...inline TInt Price() const {return (iPrice);};private:TInt iPrice;};Other candidates for inlining include trivial T class constructors andtemplated functions, particularly when using the thin template idiomdiscussed in Chapter 19.Reuse CodeCode reuse has the benefit that you get to use tried and tested code. Thisis great if the solution fits your problem exactly, but how many times haveyou found that it doesn’t quite do what you want? Often you may findyourself writing code from scratch or copying and reworking code from aprevious solution to solve your problem.
Both courses are understandableif the prior solution doesn’t fit exactly, but it is wasteful of space to endup with two or more different solutions to a common problem.1See Item 33 of Effective C++: 50 specific ways to improve your programs and designs,details of which can be found in the Bibliography.REDUCE THE SIZE OF PROGRAM CODE319Sometimes it may be possible to abstract the common code into autility module which can be used by both solutions.
Of course, this isn’talways possible, since you may not be able to modify the original source.If you cannot modify the original code, do you have to copy it for yoursolution, or can you use it and perform additional tasks to get the resultyou need? If you do have to copy and modify the code, try to copy onlyas much as is required, and make sure you adapt it where you need to,rewriting it completely if necessary so it is efficient for the task you aretrying to achieve.When writing a class, you should endeavor to make it easy to use andthus to reuse.
Sometimes code is dismissed as being impossible to reusebecause it’s so hard to work out what it’s doing or how best to use it. Thisis partly a problem with the language, because C++ is complex and it iseasy to use it incorrectly. Chapter 20 discusses some of the ways to definea clear and efficient API, which will make your life easier in the testing,maintenance and documentation phases and will improve the chancesthat potential clients will adopt it, rather than duplicate effort and codeby writing their own version.Refactor CodeMartin Fowler describes the concept of code refactoring in his bookRefactoring: Improving the Design of Existing Code, details of which canbe found in the Bibliography. As a module evolves, it may begin to loseits structure as new functions are added to enhance and extend the program.
Even well-designed code, which initially performed a specific jobwell, becomes increasingly complex when modified to support additionalrequirements built on top of the original design. In theory, the programshould be redesigned rather than simply extended. However, this usually doesn’t happen for numerous reasons, often related to the releaseschedule, which may be unprepared for redesign and reimplementationof code which is apparently fit-for-purpose.Fowler suggests that refactoring techniques can be used as a trade-offbetween the long-term pain of additional code complexity and the shortterm pain of redesign, such as the introduction of new bugs and glitches.Refactoring doesn’t usually change the functionality of existing code but,rather, changes its internal structure to make it easier to work with as itevolves and is extended.
The changes come through small steps, suchas renaming methods, abstracting similar code from two classes into aseparate shared superclass or moving member variables between classeswhen ownership changes become appropriate. The idea of refactoring isnot to extend the code itself; this should be a distinctly separate operationto meet new requirements. But refactoring should take place whenevercode is extended or bugs are fixed; it’s an ongoing process to reducecomplexity and increase code reuse. You should make sure you have320GOOD CODE STYLEgood test code in place when you refactor and run the tests after eachmodification to ensure that you do not introduce regressive bugs intothe code.Remove Debug, Test and Logging Code in Release BuildsChapter 16 discusses when assertion statements are appropriate in releasebuilds; otherwise, any code you have added for debugging, testing andlogging should be compiled out of release code using the appropriatemacros.
This allows you to minimize the overhead of diagnostic checking,in terms of runtime speed and code size, in production code.21.2Use Heap Memory CarefullyMemory management is an important issue on Symbian OS, whichprovides an infrastructure to help catch memory leaks, use memory efficiently and test that code fails gracefully under low-memory conditions.I’ll discuss each of these further in this section. The issue of good memorymanagement is so crucial to writing effective code on Symbian OS thatyou’ll find most chapters in this book touch on it to some extent.
Thissection references those other chapters where relevant.Prevent Memory LeaksIn Chapter 3, I described why you should use the cleanup stack and howto do so in order to handle leaves safely. The general rule to follow isthat, when writing each line of code, you should ask yourself: ”Can thiscode leave? If so, will all the currently allocated resources be cleanedup?” By ensuring that all resources are destroyed in the event of a leave,you are preventing potential memory leaks when exceptions occur.
Thisincludes using the two-phase construction idiom for C classes, becausea C++ constructor should never leave. I discussed this in more detail inChapter 4.You should, of course, ensure that all resources are cleaned up undernormal conditions too. For example, every resource an object ownsshould be freed, as appropriate, in a destructor or cleanup function. It’s acommon mistake to add a member variable to a class but to forget to addcorresponding cleanup code to the destructor.
I described how to checkthat your code doesn’t leak in Chapter 17.Be EfficientAs well as preventing leaks, you should use memory efficiently to preventwasting the limited resources available on a typical mobile phone. ForUSE HEAP MEMORY CAREFULLY321example, you may want to consider whether the object size of your heapbased classes can be reduced. Possible slimming techniques includereplacing a number of boolean flags with a bitfield and checking that youare using the Symbian OS container classes efficiently.2 You should alsocheck that multiple copies of filenames, or other string data, are not copiedunnecessarily between components in a module – perhaps passing theminstead as reference parameters between components, where possible, toavoid duplication.There are many other ways to reduce memory use, but it’s alsosensible to consider how complex your code may become.
There maybe a degree of compromise between preserving the readability of yourcode and shaving off some extra bytes of memory consumption. As I’vealready mentioned, it’s important to remember that if your code is hardto understand, it is less likely to be reused. Potential clients may prefer tore-implement code rather than spend time figuring out how it works, andworrying about how they will maintain it.By extension, you should prefer to use the classes provided by Symbian OS, rather than re-implement your own version of, for example, astring class. As I described in Chapters 5 and 6, the descriptor classeswere carefully designed for memory efficiency.