Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 13
Текст из файла (страница 13)
(An exceptionto this is the closing bracket in a class declaration, which may beimmediately preceded by its mandatory semicolon on the same line.)In addition, the following rules apply for line breaks:• There should be no more than one statement (e.g. function callor assignment or comparison) per line. As a rule of thumb, a linethat contains an if statement or a curly bracket should contain nosemicolons and a line that contains a for statement is the only onenormally allowed to contain two semicolons. All other lines containinga statement would normally contain exactly one semicolon.• A statement may be broken into multiple lines if it is too long to readcomfortably (i.e.
if it would otherwise require horizontal scrolling).• A semicolon must always be followed by a line break.There are also some rules regarding spacing, although they tend notto be followed as strictly as the rules for indentation and bracketing, sowe do not present them here. Indentation and bracketing change theappearance of the source code and affect the readers’ ability to find(easily) where a block starts and ends.
The choice of whether to insert aspace or not is unlikely to affect the overall appearance of the code andis largely a matter of personal taste.The following is a piece of code from the noughts-and-crosses gamethat conforms to the naming, indentation and bracketing rules of thissection. Do not worry too much about what it is supposed to be doing (inthis case, nothing really), as at this stage the layout is the important thingto demonstrate:48SYMBIAN OS C++class COandXEngine : public CBase/*This engine class represents the board, a grid of tileswhich can be modified by the controller.*/{public:static COandXEngine* NewL();virtual ∼COandXEngine();private:COandXEngine();TInt TileState(TInt aX, TInt aY) const;public:// resetvoid Reset();TInt SquareStatus(TInt aIndex) const;// stuffTBool TryMakeMove(TInt aIndex, TBool aCrossTurn);TInt GameWonBy() const;// persistencevoid ExternalizeL(RWriteStream& aStream) const;void InternalizeL(RReadStream& aStream);private:/*Each tile’s value is represented by an integer,ETileBlank, ETileNought, or ETileCross.*/TFixedArray<TInt, KNumberOfTiles> iTileStates;};TInt COandXEngine::TileState(TInt aX, TInt aY) const/*Get the state of the tile at the supplied coordinates.@param aXTile X coordinate.@param aYTile Y coordinate.@returnValue of tile at (aX, aY).*/{ASSERT(aX >= 0 && aX < KTilesPerSide);ASSERT(aY >= 0 && aY < KTilesPerSide);return iTileStates[aY * KTilesPerSide + aX];}TInt COandXEngine::GameWonBy() const/*Check if there is a full line of noughts or crosses.The line can be horizontal, vertical, or diagonal.@return*/ETileNought if there is a line of noughts;ETileCross if there is a line of crosses;Zero if there is no complete line.FUNCTIONS49{const TInt KNoughtWinSum = KTilesPerSide * ETileNought;const TInt KCrossWinSum = KTilesPerSide * ETileCross;// is there afor (TInt i ={TInt rowSumTInt colSumfor (TInt j{rowSum +=colSum +=}row or column of matching tiles?0; i < KTilesPerSide; ++i)= 0;= 0;= 0; j < KTilesPerSide; ++j)TileState(j, i);TileState(i, j);if (rowSum == KNoughtWinSum | | rowSum == KCrossWinSum)return rowSum / KTilesPerSide;if (colSum == KNoughtWinSum | | colSum == KCrossWinSum)return colSum / KTilesPerSide;}// is there a diagonal of matching tiles?TInt blTrSum = 0;// bottom left to top rightTInt tlBrSum = 0;// top left to bottom rightfor (TInt i = 0; i < KTilesPerSide; ++i){tlBrSum += TileState(i,i);blTrSum += TileState(i,KTilesPerSide - 1 - i);}if (blTrSum == KNoughtWinSum | | blTrSum == KCrossWinSum)return blTrSum / KTilesPerSide;if (tlBrSum == KNoughtWinSum | | tlBrSum == KCrossWinSum)return tlBrSum / KTilesPerSide;return 0;}3.3 FunctionsFunction prototypes in C++ header files can convey a lot of information, including:• whether the function is imported from a DLL (indicated by IMPORT_C),is inline and expanded from a header, or is private to a DLL• whether the function is public, protected, or private in theC++ sense (you have to scan up the file to see this, but this informationis effectively part of the prototype even so)• whether the function is virtual (you have to scan down the base classesto be sure about this, but this information is part of the signature) and,if virtual, whether it is also pure virtual50SYMBIAN OS C++• whether the function is static• the return type (or void)• the name – usually a good hint at what the function does• whether the function can leave (L at the end of the name)• the type and method of passing for all the arguments (optionally, witha name that hints at purpose, although the name is not formally partof the signature)• whether there are any optional arguments• whether it is const.If a function and its arguments (and class) have been named sensibly,and if the right type of parameter passing has been used, you can oftenguess what a function does by looking at its prototype.
For example, thefollowing function is the basic function for writing data to a file – you caneven guess that TDesC8 is a type suitable for data buffers by looking atthis signature:TInt RFile::Write(const TDesC8& aBuffer)The TInt return is an error code, while the aBuffer parameter is adescriptor containing the data and is not modified by the function.Most of this is standard C++ material. The exceptions are leaving functions, which we’ve already mentioned (and explain fully in Chapter 4)and the naming conventions associated with DLLs. These are very important and aren’t covered by C++ standards; we cover the significance ofIMPORT_C later in this chapter.Each parameter’s declaration gives valuable information about whetherthat parameter is to be used for input or output, and a clue about whetherthe parameter is large or small.
If a parameter is of basic type X, there arefive ways to specify it in a signature, as shown in Table 3.4.Table 3.4 Parameter specifications.By valueInputOutputXBy & referenceconst X&X&By * referenceconst X*X*For input parameters, there is a fundamental distinction betweenpassing by value and passing by reference. When you pass by value, C++copies the object into a new stack location before calling the function.You should pass by value only if you know the object is small – a built-inAPIS51type, say, or something that is shorter than two machine words (64 bits).If you pass by reference, only a 32-bit pointer is passed, regardless of thesize of the data.If you pass by reference, you have to choose between * and &references. Usually an & reference is better. Effectively it treats theargument variable as a synonym of the variable in the calling functionand operations affect the original variable contents directly.
In contrast,a pointer (*) reference passes the address of the variable into thefunction, which then has to be dereferenced on every occasion usingthe appropriate operator (->). Use a pointer reference if you have to,especially where a null value is possible, or you’re transferring ownershipof the object. It’s more usual to pass C class instances with * and to passR and T class instances directly or with &.You have to use an & reference for C++ copy constructors andassignment operators, but it’s rare to need to code such things in SymbianOS. Some Symbian OS APIs use an & reference for C types to indicatethat a null value is not acceptable.3.4 APIsIf you have a component, then its API allows you to use it.
In addition, itsAPI may also allow it to call your code. In the old days, components weresimple libraries; they would specify library functions that you would callto get the components to do what you wanted.Event-driven GUI systems are often associated with frameworks, whichcall your code to allow it to do things supported by the framework. Thecomponent specifies framework functions and you implement them.Framework functions used to be called callbacks: the basic theorywas that your code was really in control, but the library needed to callit back occasionally so it could complete a function for the library.
Butthe truth these days is that the framework is essentially in control and itenables you to do things. The framework functions are the main functionsthat allow you to do anything. ‘Callback’ is quite an inappropriate termfor this. The word is not used for the major Symbian OS frameworks;it is still used for a couple of situations where the old callback scenario really applies and in relation to a couple of the oldest classes inSymbian OS.We can loosely classify a class or even an entire API as either a libraryAPI or a framework API. A library API mainly contains functions thatyour code calls, while a framework API consists mainly of functions thatcall your code.