Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CQG / Задание1 / HDS_VCPP_Programming Rules.doc
Скачиваний:
9
Добавлен:
16.04.2013
Размер:
327.68 Кб
Скачать

C. Examples

Example 1.Documentation of a file.

////////////////////////////////////////////////////////////////

// The Any File

//

// File: AnyClass.h

//

// Description:

// This is a test program

Example 2.Documentation of a class.

////////////////////////////////////////////////////////////////

// CAnyClass

//

// Parameters:

// ParClass: The class for the first parameter of the class

// template.

// Description:

// This is a test class

// Prerequisites:

// Requires a dialog template.

template <ParClass>

class CAnyClass

{

...

};

Example 3.Documentation of a (member) function.

// AnyClass.h

class CAnyClass

{

public:

// Parameters:

// [in] In: This is an input parameter;

// [out] Out: This is an output parameter;

// [in/out] InOut: This is an input/output parameter

// Returns: Any value

// Throws: AnyException: This is a throwable exception

// Effect: Does nothing

int AnyFunction(int In, int& Out, int& InOut)

throw(AnyException);

...

};

Example 4.Comments for unfinished code.

// !!! Requires COMCTL32.DLL ver. 4.71.

// If you use it, remove the comments

// pWnd->SendMessage(TVM_SETBKCOLOR, 0,

// (LPARAM)::GetSysColor(COLOR_WINDOW));

Example 5.Strategic and tactical comments.

// The next two lines are strategic comments.

// This loop does some complicated things. It works like this:

// blah-blah-blah...

int i = 0;

for (;;)

{

int index = i++ + ++i * i-- - --i; // a tactical comment

}

Example 6.Technique for preventing multiple inclusion of an include file.

#if _MSC_VER > 1000 // this is MS Visual C++ specific

#pragma once

#endif

#ifndef __FOO_H__

#define __FOO_H__

// The rest of the file

...

#endif // __FOO_H__

Example 7.Include file for the class CPackableString.

// PackableString.h

#ifndef __PACKABLESTRING_H__

#define __PACKABLESTRING_H__

// Do not include stdafx.h

#include "Packable.h"

// It is not necessary to declare class CBuffer when each

// pointer declaration specifies the keyword class as shown

// below. An explicit declaration makes the code easier

// to understand.

class CBuffer;

class CPackableString : public CString, public CPackable

{

public:

CPackableString(const CString& str) throw(CMemoryException*);

class CBuffer* Put(class CBuffer* pOutBuffer) throw();

...

};

#endif // __PACKABLESTRING_H__

Example 8.Implementation file for the class CPackableString.

// File: PackableString.cpp

#include "stdafx.h"

// To be able to use CBuffer instances, Buffer.h

// MUST be included

#include "Buffer.h"

#include "PackableString.h"

CBuffer* CPackableString::Put(CBuffer* pOutBuffer) throw()

{

...

}

Example 9.(MFC specific) Local constant string for identifying implementation files.

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

Example 10.Choice of names.

int nGroupID; // Instead of nGrpID

int nNameLength; // Instead of nNamLn

Example 11.Ambiguous names.

void TermProcess(); // Terminate process or terminal process?

Example 12.Names having numeric characters can cause errors that are difficult to locate.

int I0 = 13; // Names with digits can be

int IO = I0; // difficult to read.

Example 13.Declarations in the class library Emc2.

#define EMC2_ANYDEFINE 0

extern "C" void Emc2CStyleFunc();

namespace Emc2

{

class CAnyClass

{

public:

CAnyClass() throw();

...

protected:

int m_nID;

...

};

};

// Or (if namespaces are not supported)

class CEmc2AnyClass

{

public:

CEmc2AnyClass() throw();

...

protected:

int m_nID;

...

};

Example 14.Turn off namespaces if necessary.

// atlbase.h

#ifndef ATL_NO_NAMESPACE

#ifndef _ATL_DLL_IMPL

namespace ATL

{

#endif

#endif

...

#ifndef ATL_NO_NAMESPACE

}; //namespace ATL

using namespace ATL;

#endif

Example 15.A class declaration in accordance with the style rules.

class CString

{

// Methods

public:

// Constructors/destructors

// Parameters:

// Returns:

// Throws:

// Effect: Constructs the object

CString() throw();

// Parameters:

// Returns:

// Throws: CMemoryException*: If there is no more free memory

// Effect: Copy constructs the object

CString(const CString& str) throw(CMemoryException*);

// Attributes

int GetLength() const throw()

// Parameters:

// Returns: String length

// Throws:

// Effect:

{

return m_nLength;

}

...

// Operations

...

// Overridables

...

// Implementation

...

// Data

protected:

int m_nLength; // String length

...

};

Example 16.Declaration of formal arguments.

int SetPoint(int, int); // No!

int SetPoint(int x, int y); // Better

int SetPoint(int x, int y)

{

...

}

Example 17.Function implementation.

int GetLength() const {...} // No!

int GetLength() const // Good

{

...

}

Example 18.Exception specification.

void Foo(); // Not recommended

void Foo() throw(); // Good. You explicitly state that Foo

// throws no exceptions.

Example 19.The left parenthesis always directly after the function name, the right one after the last parameter. The arguments are separated by spaces.

void Foo ( int nFoo1,int nFoo2); // No!

void Foo(int nFoo1, int nFoo2); // Better

Example 20.Flow control structure without statements.

// No block at all - No!

while (/* something */);

// Block on the same line – No! See Rule 34

while (/* something */) {}

// Empty block - better!

while (/* something */)

{

// Empty!

}

Example 21.*and&together with the type.

int* CObject::AsInt()

{

...

};

char* pszUserName = NULL;

int nVariable = 42;

int& nRef = nVariable;

Example 22.Declaration of several variables in the same statement.

// NOT RECOMMENDED

int* p, i; // p is declared pointer to int,

// while i is declared int

Example 23.How to use spaces around operators.

int nWrong= ++ i+( pClass -> m_n<<2 );Foo( nWrong ) ; // No!

int nRight = ++i + (pClass->m_n << 2); // Good

Foo(nRight); // the next statement is on the next line

Example 24.The right way of using accessors.

class CPriority : public CObject {...};

class CProccess

{

public:

CPriority* GetPriority()

{

ASSERT_VALID(m_pPriority);

...

return m_pPriority;

}

...

protected:

CPriority* m_pPriority; // priority data

};

Example 25.const-declared access functions to internal data in a class.

class CSpecialAccount : public CAccount

{

public:

int InsertMoney();

// int GetAmountOfMoney(); Not recommended! Forbids ANY

// constant object to access

// the amount of money

int GetAmountOfMoney() const; // Better!

...

protected:

int m_nMoneyAmount;

};

Example 26.Overloading an operator/function with respect to const-ness.

#include <windows.h>

#include <tchar.h>

class CBuffer

{

public:

CBuffer(LPCTSTR psz);

{

_tcsncpy(m_szBuffer, psz, sizeof(m_szBuffer));

}

// A. Non-const member functions: result is an lvalue

TCHAR& operator[](UINT nIndex)

{

return m_szBuffer[nIndex];

}

// B. Const member functions: result is not an lvalue

TCHAR operator[](UINT nIndex) const

{

return m_szBuffer[nIndex];

}

protected:

TCHAR m_szBuffer[_MAX_PATH];

};

const CBuffer bufPeter(_T("Peter")); // this is a constant

// buffer

CBuffer bufMary(_T("Mary")); // this buffer can change

// calls TCHAR& CBuffer::operator[](UINT)

bufMary[2]=_T('c');

// calls CBuffer::operator[](UINT) const

bufPeter[2] = _T('c'); // ERROR: bufPeter[2] is not an lvalue

Example 27.“Dangerous” class whose constructor can throw an exception.

#include <comdef.h>

class CInternalData {...};

class CDangerous

{

public:

CDangerous(const char* psz) throw(_com_error); // Constructor

~CDangerous() throw(); // Destructor

...

protected:

CInternalData* m_pData;

_bstr_t m_bstr;

};

CDangerous::CDangerous(const char* psz) throw(_com_error)

{

m_pData = new CInternalData();

m_bstr = psz; // An exception can be thrown!

// In that case, m_pData will never be freed

}

CDangerous::~CDangerous() throw()

{

if (m_pData != NULL)

delete m_pData;

}

Example 28.“Safe” class whose constructor throws no exceptions.

#include <comdef.h>

#include <crtdbg.h>

class CInternalData {...};

class CSafe

{

public:

CSafe() throw() {}; // Constructor

~Safe() throw(); // Destructor

// Initialization

void Init(const char* psz) throw(_com_error);

...

protected:

CInternalData* m_pData;

_bstr_t m_bstr;

};

void CSafe::Init(const char* psz) throw(_com_error)

{

_ASSERTE(m_pData == NULL);

_ASSERTE(!m_bstr);

m_pData = new CInternalData();

m_bstr = psz; // An exception can be thrown!

// But, nevertheless, m_pData will be freed

}

CDangerous::~CDangerous() throw()

{

if (m_pData != NULL)

delete m_pData;

}

Example 29.Definitions of classes that does not have virtual destructors.

class CFruit

{

public:

~CFruit(); // forgot to make destructor virtual!

...

};

class CApple : public CFruit

{

public:

~CApple(); // Destructor

...

};

// "Dangerous" usage of pointer to base class

class CFruitBasket

{

public:

CFruitBasket() : m_nStoredFruits(0) {}

~CFruitBasket(); // delete all fruits

// add instance allocated on the free store

void Add(CFruit* pFruit);

{

// store pointer to fruit

m_rgFruits[m_nStoredFruits++] = pFruit;

}

...

protected:

CFruit* m_rgFruits[42]; // Max 42 fruits stored

int m_nStoredFruits;

};

CFruitBasket::~CFruitBasket()

{

while (m_nStoredFruits > 0)

{

// only CFruit::~CFruit() is called!

delete m_rgFruits[--m_nStoredFruits];

}

}

Example 30.Definition of a “dangerous” class that does not have a copy constructor.

#include <windows.h>

#include <tchar.h>

// "Dangerous" CString class

class CString

{

public:

CString(LPCTSTR psz = NULL); // Constructor

~CString(); // Destructor

...

protected:

LPTSTR m_pszData;

...

};

CString::CString(LPCTSTR psz)

{

if (psz != NULL)

m_pszData = new TCHAR[lstrlen(psz) + 1];

else

m_pszData = NULL;

if (m_pszData != NULL)

_tcscpy(m_pszData, psz);

}

CString::~CString()

{

if (m_pszData != NULL)

delete[] m_pszData;

}

CString str1(_T("Dangerous"));

// WARNING: IN A BITWISE COPY OF str1::m_pszData,

// THE DESTRUCTOR FOR str1::m_pszData WILL BE CALLED TWICE:

// FIRST, WHEN str1 IS DESTROYED; AGAIN,

// WHEN str2 IS DESTROYED.

CString str2 = str1;

Example 31.“Safe” class that has both default and copy constructors.

#include <windows.h>

#include <tchar.h>

// "Safe" CString class

class CString

{

public:

CString(LPCTSTR psz = NULL); // Constructor

CString(const CString& str); // Copy constructor

~CString(); // Destructor

...

protected:

LPTSTR m_pszData;

...

};

CString::CString(LPCTSTR psz)

{

if (psz != NULL)

m_pszData = new TCHAR[lstrlen(psz) + 1];

else

m_pszData = NULL;

if (m_pszData != NULL)

_tcscpy(m_pszData, psz);

}

CString::CString(const CString& str )

{

if (str.m_pszData != NULL)

m_pszData = new TCHAR[lstrlen(str.m_pszData) + 1];

else

m_pszData = NULL;

if (m_pszData != NULL)

_tcscpy(m_pszData, str.m_pszData);

}

CString::~CString()

{

if (m_pszData != NULL)

delete[] m_pszData;

}

CString str1(_T("Safe"));

// SAFE COPY: CString::CString(const CString&) CALLED

CString str2 = str1;

Example 32.Dangerous use of static objects in constructors.

// Hen.h

class CEgg;

class CHen

{

public:

Hen(); // Default constructor

~Hen(); // Destructor

void MakeNewHen(CEgg*);

...

};

// Egg.h

class CEgg {...};

extern CEgg g_eggFirst; // Defined in Egg.cpp

// FirstHen.h

class CFirstHen : public CHen

{

public:

CFirstHen(); // Default constructor

...

};

extern CFirstHen g_henFirst; // Defined in FirstHen.cpp

// FirstHen.cpp

CFirstHen g_henFirst; // CFirstHen::CFirstHen() called

CFirstHen::CFirstHen()

{

// The constructor is risky because g_eggFirst is

// a global object and may not yet exist

// when m_henFirst is initialized.

// Which comes first, the chicken or the egg?

MakeNewHen(&g_eggFirst);

}

Example 33.One way of ensuring that global objects have been initialized.

// WARNING: THIS CODE IS NOT FOR BEGINNERS!!!

// PortSetup.h

class СPortSetup

{

public:

CPortSetup(); // Constructor: initializes flag

void Foo(); // Only works correctly if flag is 42

private:

int m_nFlag; // Always initialized to 42

};

// Must be initialized before use

extern CPortSetup g_objPortSetup;

// Create one instance of s_objPortSetupInit in each translation

// unit. The constructor for s_objPortSetupInit will be called

// once for each translation unit. It initializes g_objPortSetup

// by using the placement syntax for the "new" operator.

class CPortSetupInit

{

public:

CPortSetupInit();

private:

static bool s_bSetup;

};

static CPortSetupInit s_objPortSetupInit;

// PortSetup.cpp

#include <new.h>

#include "PortSetup.h"

...

CPortSetupInit::CPortSetupInit()

{

if (!s_bSetup)

{

new(&g_objPortSetup) CPortSetup;

s_bSetup = true;

}

}

Example 34.Override of virtual functions does not work in the base class constructors.

class CBase

{

public:

CBase();

virtual void Foo() {...}

...

};

CBase::CBase()

{

Foo(); // CBase::Foo() is ALWAYS called

}

// derived class overrides Foo()

class CDerived : public CBase

{

public:

virtual void Foo() {...}

...

};

CDerived d; // CBase::Foo() called when the CBase-part of

// CDerived is constructed.

Example 35.Definition of a class with an overloaded assignment operator.

class CDangerousBlob

{

public:

const CDangerousBlob& operator=(const CDangerousBlob& blob);

...

protected:

BYTE* m_pBlob;

};

// definition of assignment operator

const CDangerousBlob&

CDangerousBlob::operator=(const CDangerousBlob& blob)

{

if (this != &blob) // guard against assigning to

{ // the "this" pointer

delete m_pBlob; // disastrous if this == &blob

}

...

}

Example 36.Incorrect and correct return values from an assignment operator.

// Well?

void CMySpecialClass::operator=(const CMySpecialClass& msp);

CMySpecialClass&

CMySpecialClass::operator=(const CMySpecialClass& msp); // No!

// Recommended

const CMySpecialClass&

CMySpecialClass::operator=(const CMySpecialClass& msp);

Example 37.Never return a non-const reference to member data from a public function.

class CAccount

{

public:

CAccount(int nMyMoney) : m_nMoneyAmount(nMyMoney) {}

const int& GetSafeMoney() const { return m_nMoneyAmount; }

int& GetRiskyMoney() const { return m_nMoneyAmount; } // No!

...

protected:

int m_nMoneyAmount;

};

CAccount accMy(10); // I'm a poor lonesome programmer

// a long way from home

accMy.GetSafeMoney() += 1000000; // Compilation error:

// assignment to constant

accMy.GetRiskyMoney() += 1000000; // accMy::m_nMoneyAmount =

// 1000010!

Example 38.An explicit specialization of a template for a specific type.

template <class T> void Foo(T t) {...}

void Foo(int i) {...} // No!

template<> void Foo<int>(int i) {...} // Good

Example 39.Problem when using parameterized types.

template <class T>

class CConflict

{

public:

void Foo(int i);

void Foo(T t); // What if T is an int or another

// integral type?

// The compiler will discover this, but...

};

Example 40.References instead of pointers.

// unnecessarily complicated use of pointers

void AddOneComplicated(int* pIntegerPointer)

{

*pIntegerPointer += 1;

}

AddOneComplicated(&i);

// write this way instead:

void AddOneEasy(int& nIntegerReference)

{

nIntegerReference += 1;

}

AddOneEasy(i);

Example 41.Different mechanisms for passing arguments.

// A. A copy of the argument is created on the stack.

// The copy constructor is called on entry,

// and the destructor is called at exit from the function.

// This may lead to very inefficient code.

void CallByValue(CString str);

CString str;

CallByValue(str); // call-by-value

// B. The actual argument is used by the function

// and it can be modified by the function.

void CallByReference(CString& str);

CString str;

CallByReference (str); // call-by-reference

// C. The actual argument is used by the function

// but it cannot be modified by the function.

void CallByConstReference (const CString& str);

CString str;

CallByConstReference(str); // call-by-constant-reference

// D. A pointer to the actual argument is used by the function.

// May lead to messy syntax when the function uses

// the argument.

void CallByConstPointer(const CString* p);

CString str;

CallByConstPointer(&str); // call-by-constant-pointer

Example 42.Example of the proper usage of function overloading.

class CString

{

public: // Used like this:

// CString s = _T("abc123");

int Contains(const TCHAR ch); // int i = s.Contains(_T('b'));

int Contains(LPCTSTR psz); // int i = s.Contains(_T("bc1"));

int Contains(const CString& str); // int i = s.Contains(str);

...

};

Example 43.Inline functions are better than macros.

// Example of problems with #define "functions"

#define SQUARE(x) ((x)*(x))

int i = 2;

int j = SQUARE(i++); // j = (2 * 3) = 6

// Inline functions are safer and easier to use than macros

// if you need an ordinary function that would have been

// unacceptable for efficiency reasons. They are also easier

// to convert to ordinary functions later on.

inline int Square(int i)

{

return (i * i);

};

int i = 2;

int j = Square(i++); // j = (2 * 2) = 4

Example 44.Temporary objects and one way of eliminating them.

class CBigObject { double m_rgBig[123456]; };

// Example of a very inefficient function

// with respect to temporary objects

CBigObject SlowTransform(CBigObject bo)

{

// When entering SlowTransform(), bo is a copy

// of the function argument provided by the user. -

// A copy constructor for CBigObject is executed.

// ... Transform bo in some way

return bo; // Transformed bo is returned to the user

}

// When exiting SlowTransform(), a copy of bo is returned to the

// user - copy-constructor for CBigObject is executed again.

// Much more efficient solution

CBigObject& FastTransform(CBigObject& bo)

{

// When entering FastTransform(), bo is the same object as

// the function argument provided by the user. -

// No copy-constructor is executed.

// ... Transform bo in some way

return bo; // Transformed bo is returned to the user.

}

// When exiting FastTransform(), the very same bo is returned

// to the user. - No copy constructor executed.

CBigObject bo;

bo = SlowTransform(bo);

bo = FastTransform(bo); // same syntax as SlowTransform()!

Example 45.Different ways of declaring constants.

// constants using macros

#define BUFSIZE 7 // no type checking

// constants using const

const int nBufSize = 7; // type checking takes place

// constants using enums

enum { BufSize = 7 }; // type checking takes place

Example 46.Declaration of a constant defined in another file.

extern const TCHAR g_chConstantCharacter;

extern const CString g_strFileName;

Example 47.Initialization instead of assignment.

class CSpecial // array of this class is used to initialize

{ // CMyClass::m_nComplicated

public:

CSpecial(); // Default constructor

bool IsValid() const;

int Value() const;

};

const int g_nMagic = 1066;

CSpecial g_rgSpecialInit[g_nMagic];

class CMyClass

{

public:

CMyClass(LPCTSTR psz); // constructor

...

protected:

CString m_str;

int m_nComplicated;

};

// Do not do this! Inefficient code. Empty constructor +

// assignment operator called for m_str

// CMyClass::CMyClass(LPCTSTR psz)

// {

// m_str = psz;

// ...

// }

CMyClass::CMyClass(LPCTSTR psz)

: m_str(psz) // Better

{

// special case - complicated expression

for (int i = 0; i < g_nMagic; i++) // No! You must enclose

if (g_rgSpecialInit[i].IsValid()) // "for" loops in braces!

{ // See Rule 35

m_nComplicated = g_rgSpecialInit[i].Value();

break;

}

}

Example 48.Right and wrong ways of declaring variables.

// Do not do this!

// int i;

// ... 1000 lines of code

// i = 10;

int i = 10; // Better

Example 49.Different comparisons of pointers.

LPTSTR psz = new TCHAR[100];

if (!psz) // No! See Rule 55

_RPT0(_CRT_WARN, "New failed!");

if (psz == 0) // No!

_RPT0(_CRT_WARN, "New failed!");

if (psz == NULL) // Good

_RPT0(_CRT_WARN, "New failed!");

Example 50.Pointers to pointers are often unnecessary.

void PrintMatrix(int** ppMatrix, int nDim1, int nDim2)

{

for (int i = 0; i < nDim1; i++)

{

for (int j = 0; j < nDim2; j++ )

{

cout << " " <<

(reinterpret_cast<int*>(ppMatrix))[i* nDim2+j];

}

cout << endl;

}

}

// could be written as

class CMatrix

{

public:

CMatrix(int nDim1, int nDim2);

int Value(int i, int j) const;

int Dim1() const;

int Dim2() const;

...

};

void PrintMatrix(const CMatrix& matrix)

{

for (int i = 0; i < matrix.Dim1(); i++)

{

for (int j = 0; j < matrix.Dim2(); j++ )

cout << " " << matrix.Value(i, j);

cout << endl;

}

}

Example 51.Complicated declarations.

// Func1 is a function: int - (function : const char* - int)

// i.e. a function having one argument of type int and

// returning a pointer to a function having one argument of

// type const char* and returning an int.

int (*Func1(int))(const char*);

// Func2 of the same type as Func1

typedef int FTYPE(const char*);

FTYPE* Func2(int);

int (*(*pfnFunc1)(int))(const char*) = Func2;

// Realistic example from signal.h

void (*signal(int, void (*)(int)))(int);

Example 52.Syntax simplification of function pointers, using atypedef.

#include <math.h>

// Ordinary messy way of declaring pointers to functions:

// double (*MathFunc)(double) = sqrt;

// With a typedef, life is filled with happiness

// (chinese proverb):

typedef double MathFuncType(double);

MathFuncType* MathFunc = sqrt;

// You can invoke the function in an easy or complicated way

double dblReturn1 = MathFunc(23.0); // Easy way

double dblReturn2 = (*MathFunc)(23.0); // No! Correct,

// but complicated

Example 53.Using C++ casting operators.

class CBase {...};

class CDerived : public CBase {...};

CDerived* pd;

CBase* pb1 = pd;

CBase* pb2;

CDerived* pCast1 = (CDerived*)pb1; // No!

CDerived* pCast2 = static_cast<CDerived*>(pb1); // Better

// the same as pCast2

CDerived* pCast3 = dynamic_cast<CDerived*>(pb1);

CDerived* pCast4 = static_cast<CDerived*>(pb2); // No!

// pb2 is not a pointer to CDerived,

// but pCast4 != NULL!

CDerived* pCast5 = dynamic_cast<CDerived*>(pb2); // Better

Example 54.For more efficient execution, remove const-ness when storing intermediate results.

// This code is NOT recommended

#include <math.h>

#include <crtdbg.h>

class CVector

{

public:

CVector(int nSize, const int rgSrc[]); // constructor

~CVector(); // destructor

double Length() const; // Length =

// sqrt(m_pData[1]*m_pData[1] + ...)

void Set(int nIndex, int nValue);

...

protected:

int m_nSize;

int* m_pData;

double m_dblLengthCache; // to cache calculated length

bool m_bChanged; // Is it necessary to re-calculate length?

};

CVector::CVector(int nIndex, const int rgSrc[])

: m_nSize(nSize), m_pData(new int[nSize]),

m_bChanged(true), m_pdblLengthCache(0)

{

for (int i = 0; i < nSize; i++)

{

m_pData[i] = rgSrc[i];

}

}

CVector::~CVector()

{

delete[] m_pData;

}

double CVector::Length() const

{

if (bChanged) // Do we need to re-calculate length?

{

// No! Cast away const

const_cast<CVector*>(this)->m_bChanged = true;

double dblQuadLength = 0;

for (int i = 0; i < m_nSize; i++)

{

dblQuadLength += pow(m_pData[i], 2);

}

const_cast<CVector*>(this)->m_dblLengthCache =

sqrt(dblQuadLength); // No! Cast away const

}

return m_dblLengthCache;

}

void CVector::Set(int nIndex, int nValue)

{

_ASSERTE(nIndex >= 0 && nIndex < m_nSize);

m_pData[nIndex] = nValue;

m_bChanged = true;

}

Example 55.Alternative to removing const-ness for more efficient execution.

// This code is safer than Example 54but could be inefficient

#include <math.h>

#include <crtdbg.h>

class CVector

{

public:

CVector(int nSize, const int rgSrc[]); // constructor

~CVector(); // destructor

double Length() const; // Length =

// sqrt(m_pData[1]*m_pData[1] + ...)

void Set(int nIndex, int nValue);

...

protected:

int m_nSize;

int* m_pData;

double* m_pdblLengthCache; // to cache calculated length

bool* m_pbChanged; // Is it necessary to re-calculate length?

};

CVector::CVector(int nIndex, const int rgSrc[])

: m_nSize(nSize), m_pData(new int[nSize]),

m_pbChanged(new bool(true)), m_pdblLengthCache(new double)

{

for (int i = 0; i < nSize; i++)

{

m_pData[i] = rgSrc[i];

}

}

CVector::~CVector()

{

delete[] m_pData;

delete m_pbChanged;

delete m_pdblLengthCache;

}

double CVector::Length() const

{

if (m_pChanged != NULL) // Do we need to re-calculate length?

{

*m_pbChanged=false;

double dblQuadLength = 0;

for (int i = 0; i < m_nSize; i++)

{

dblQuadLength += pow(m_pData[i], 2);

}

*m_pdblLengthCache = sqrt(dblQuadLength);

}

return *m_pdblLengthCache;

}

void CVector::Set(int nIndex, int nValue)

{

_ASSERTE(nIndex >= 0 && nIndex < m_nSize);

m_pData[nIndex] = nValue;

*m_pbChanged = true;

}

Example 56.Constructors with a single argument that may imply dangerous type conversions.

class CString

{

public:

CString(int nLength); // allocation constructor

...

};

// Function that receives an object of type CString

// as an argument

void Foo(const String& s);

// Here we call this function with an int as argument

int i = 100;

Foo(i); // implicit conversion: Foo(CString(i));

Example 57.A use of implicit type conversion.

class CString

{

public:

CString(LPCTSTR psz); // constructor

operator LPCTSTR() const; // conversion operator to LPCTSTR

...

};

void Foo(const String& str);

void Bar(LPCTSTR psz);

Foo(_T("Hello!")); // implicit type conversion

// LPCTSTR - CString

CString strPeter(_T("pan"));

Bar(strPeter); // implicit type conversion CString - LPCTSTR

Example 58.Addition that leads to a compile-time error.

class CString

{

public:

CString(LPCTSTR psz); // constructor

operator LPCTSTR() const; // conversion operator to LPCTSTR

...

};

void Foo(const String& str);

void Bar(LPCTSTR psz);

class CWord

{

public:

CWord(LPCTSTR psz); // constructor

...

};

// function Foo overloaded

void Foo(const CWord& word);

// ERROR: Foo(_T("Hello!")) MATCHES BOTH:

// void Foo(const CString&);

// AND void Foo(const CWord&);

Foo(_T("Hello!")); // ERROR: ambiguous type conversion!

CString strPeter(_T("pan"));

Bar(strPeter); // implicit type conversion CString - LPCTSTR

Example 59.When implicit type conversion gives unpleasant results.

// This function looks bulletproof, but it isn't.

// Newer versions of compilers should flag this as an error.

void MySwap(int& i, int& j)

{

int nTemp = i;

i = j;

j = nTemp;

}

int i = 10;

unsigned int ui = 20;

MySwap(i, ui); // What really happens here is:

// int T = int(ui); // Implicit conversion

// MySwap(i, T); // ui is of course not changed!

// Fortunately, the compiler warns for this!

Example 60.Conversion of derived class pointer to a virtual base class pointer is irreversible.

class CVirtualBase

{

public:

virtual class CDerived* AsDerived() = 0;

};

class CDerived : virtual public CVirtualBase

{

public:

virtual CDerived* AsDerived();

};

CDerived* CDerived::AsDerived()

{

return this;

}

CDerived d;

CDerived* pd = NULL;

CVirtualBase* pb = dynamic_cast<CVirtualBase*>(&d);

pd = dynamic_cast<CDerived*>(pb); // ERROR! Cast from

// virtual base class pointer

pd = pb->AsDerived(); // OK! Cast in function AsDerived

Example 61.Dangerousswitch/casestatement.

switch (tag)

{

case A:

// Do something

// Next statement is a call to Foo() inside next case

case B:

Foo();

// do something else

break; // now we leave the switch-statement

default:

// If no match in above cases, this is executed

_ASSERTE(false);

}

Example 62.Use explicit comparisons for non-Boolean values.

LPTSTR pszName = new TCHAR[_MAX_PATH];

// if (p) // No!

if (p != NULL) // Good

{

...

}

Example 63.Usingbreakto exit a loop, no flags are needed.

do // This way:

{

if (/* something */)

{

// do something

break;

}

} while(someCondition);

bool bEndFlag = false; // is better than this:

do

{

if (/* something */)

{

// do something

bEndFlag = true;

}

} while(someCondition && !bEndFlag);

Example 64.Problem with the order of evaluation.

// interpreted as (a < b) < c, not (a < b) && (b < c)

if (a < b < c )

{

...

}

// interpreted as a & (b < 8), not (a & b) < 8

if (a & b < 8)

{

...

}

Example 65.When parentheses are recommended.

int i = a = b && c < d && e + f < = g + h; // No!

int j = (a = b) && (c < d) && ((e + f) <= (g + h)); // Better

Example 66.Right and wrong ways to invokedeletefor arrays with destructors.

int nSize = 7;

T* pT = new T[nSize]; // T is a type with defined

// constructors and destructors

...

delete pT; // No! Destructor only called for

// the first object

delete[10] pT; // No! Destructor called on memory

// outside the array

delete[] pT; // OK, and always safe!

Example 67.Dangerous memory management.

CString DangerousFunc(LPCTSTR psz)

{

CString* pTemp = new CString(psz);

return *pTemp;

// pTemp is never deallocated and the user of DangerousFunc

// cannot deallocate it because a temporary copy of

// that instance is returned

}

Example 68.Using resource strings.

// .RC file

STRINGTABLE DISCARDABLE

BEGIN

IDS_ERROR "Error!"

...

END

// .CPP file

TCHAR szError[] = _T("Error!"); // No!

TCHAR szError[_MAX_PATH]; // Better

LoadString(hInstance, IDS_ERROR, szError, _MAX_PATH);

Example 69.Using standard types of Win16 and Win32.

// Instead of:

unsigned long lTime;

unsigned short xMouse;

char* pszMenuName;

// Use (for example):

DWORD dwTime;

WORD xMouse;

LPTSTR pszMenuName;

Example 70.An ambiguous expression.

int i = 7;

Func(i, ++i); // No! Func could receive the values 7 and 8, or

// 8 and 8 for its parameters, depending on

// whether the parameters are evaluated from left

// to right or from right to left

Example 71.Do not depend on the order of initialization in constructors.

#include <iostream.h>

class CWrong

{

public:

CWrong(int n);

private:

int m_i;

int m_j;

};

inline CWrong::CWrong(int n)

: m_j(n), m_i(m_j) // No! m_j will be initialized after m_i!

{

cout << "m_i:" << " & " << "m_j:" << endl;

}

CWrong wrong(7); // Rather unexpected output: m_i:0 & m_j:7

Example 72.Initialization of static objects.

// Foo.h

#include <windows.h>

static const UINT s_nSize = 1024;

class CFoo

{

public:

Foo(LPCTSTR psz); // constructor

...

protected:

TCHAR m_szBuffer[s_nSize];

static UINT s_nCounter; // number of constructed CFoo's

};

extern CFoo g_foo1;

extern CFoo g_foo2;

// Foo1.cpp

#include "Foo.h"

UINT CFoo::s_nCounter = 0;

CFoo g_foo1(_T("one"));

//Foo2.cpp

#include <tchar.h>

#include "Foo.h"

CFoo g_foo2(_T("two"));

CFoo::CFoo(LPCTSTR psz) // irrational constructor

{

_tcsncpy(m_szBuffer, psz, sizeof(m_szBuffer));

switch (s_nCounter++)

{

case 0:

case 1:

::OutputDebugString(g_foo1.m_szBuffer);

::OutputDebugString(_T(","));

::OutputDebugString(g_foo2.m_szBuffer);

::OutputDebugString(_T("\n");

break;

default:

::OutputDebugString(_T("Hello, world!\n"));

}

}

// If a program using Foo.h is linked with Foo1.cpp and

// Foo2.cpp, either

// ,two or one,

// one,two one,two

// is written on debug output depending on the order

// of the files given to the linker

Example 73.Difficult error in a string class.

class CString

{

public:

operator LPCTSTR() const; // conversion operator to LPCTSTR

friend CString operator+(const CString& strLeft,

const CString& strRight);

...

};

CString a = _T("This may go to ");

CString b = _T("h***!");

// The addition of a and b generates a new temporary CString

// object. After it is converted to a LPCTSTR by the

// conversion operator, it is no longer needed and may be

// deallocated. This means that psz points to characters

// that are already deallocated. DANGEROUS!

LPTSTR psz = a + b;

10/25/17

Page 53

Этот документ представляет собственность Высшей Школы Программирования. Все права защищены  2002.