- •Introduction
- •Terminology
- •Source Code in Files Code Structure
- •Naming Files
- •Comments
- •Include Files
- •Assigning Names
- •Style Classes
- •Functions
- •Compound Statements
- •Flow Control Statements
- •Pointers and References
- •Miscellaneous
- •Classes Considerations Regarding Access Rights
- •Inline Functions
- •Friends
- •Const Member Functions
- •Constructors and Destructors
- •Assignment Operators
- •Operator Overloading
- •Member Function Return Types
- •Inheritance
- •Templates
- •Functions
- •Function Arguments
- •Function Overloading
- •Return Types and Values
- •Inline Functions
- •Temporary Objects
- •General
- •Constants
- •Variables
- •Pointers and References
- •Type Conversions
- •Flow Control Structures
- •Expressions
- •Memory Allocation
- •Fault Handling
- •Portable Code Localization
- •Data Abstraction
- •Sizes of Types
- •Type Conversions
- •Data Representation
- •Underflow/Overflow
- •Order of Execution
- •Temporary Objects
- •Pointer Arithmetic
- •References
- •Appendix a. Hungarian Notation
- •C. Examples
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;
| ||
|
Page
|
|
Этот документ представляет собственность Высшей Школы Программирования. Все права защищены 2002.