The Elements of C++ Style 笔记

《The Elements of C++ Style》 Trevor Misfeldt

读书计划: 150pages / 20pages = 8

2011.11.02 1-11

Preface

作者以前出过一本 The elements of Java style,这本应该是后续。 目标读者是懂c++的,更侧重在一个团队工作的C++开发者。

如果不懂C++,推荐的是Bjarne Stroustrup的两本书,The C++ Programming Language 和 The Design and Evolution of C++, 他是C++的设计者。

 

Introduction

Style tells you want you ought to write - what humans reading the code will understand.

 

General Principles

1. Adhere to the sytle of the original when modifying

2. Adhere to the principle of least astonishment

3. Do it right the first time, not only for product

4. Document any deviations, when you must violate a rule

 

Formatting Conventions

5. Use indented block statements

如果用一个开发环境,用默认的就好。如果是手动的,两个空格,占用少空间。

DNode::DNode() {

..if (0 != next) {

....next->prev_ = this;

..}

}

6. Indent statements after a label

void Foo:doSomething(int arg) {

..loop:

....for (int index = 0; index <= arg; index++){

 

7. Choose one style for brace placement

void sameLine() {

}

 

void nextLine()

{

}

 

2011.11.03 (12-30p, 1.5h)

8. Break long statements into multiple lines

为了保证打印依然可读,保持在最大长度,一般80-132个字符。

首先,不要在一行声明多个变量,导致长度过大。

double x = rand(); double y = rand(); // too long!

double x = rand();

double y = rand();

其次,一行有复杂的表达式,将其拆开几个,用局部变量保存。

double length = sqrt(pow(rand(), 2.0) +

pow(rand(), 2.0)); // Too long!

->

double xSquared = pow(rand(), 2.0);

double ySquared = pow(rand(), 2.0);

double length = sqrt(xSquared + ySquared);

如果上面不能处理,采取一下规则缩进:

step1:顶级表达式有1或多个逗号

double length = sqrt(pow(x, 2.0),

                               pow(y, 2.0));

step2:顶层表达式没有逗号,在最低优先级符号分开

return person1.getName() == person2.getName() &&

          person1.getAge() == person2.getAge();

step3: 重复以上步骤直到允许的最大长度。

 

9. Include White Space

在条件语句中,用一个空格去分开关键词、单括号和花括号

for.(...).{

   // ...

}

在除了.和->操作符外的其它二元操作符,前后使用一个空格

double length  = sqrt(x *x + y * y);

用反斜杠分开一个方法的不同逻辑section

用反斜杠分开每个函数的定义在源码文件

std::string Customer::getNmae(void) const {

  // ...

}

10. Do not use "hard" tabs

不用使用默认的tab,跨平台格式可能出问题,尽量用空格,

或设置编辑器配置,用空格替代tab符号

 

Naming Conventions

-Preprocessor Macro Names

11. Use UPPERCASE and Underscores for Preprocessor Macro Names

#define DEPRECATED_FEATURE

#define MIN(a,b) ((a) < (b) ? (a) : (b))

 

12. Add a Unique Prefix to Macro Names

避免与他人或第三方库冲突,不过最好是放在一个命名空间内。

 

-Type and Constant Names

13. Use "UpperCamelCase" for classes, Constants, Structures, Enumerations, and Typedefs

enum BackgroundColor {

  None,

  Red,

  Green,

  Blue

};

 

const int FixedWidth = 10;

class BankAccount {

  // ...

};

typedef list<BankAccount> Portfolio;

 

14. Use Nouns to Name Compound Types

class Customer {

  // ...

};

typedef int Dollars;

 

15. Pluralize the Names of Collections

plural 复数

class Shapes {

  public:

    int getShape(int index);

    void removeShape(int index);

    void addShape(Shape shape);

    int getCount();

};

-Function Names

16. Use "lowerCamelCase" for Function Names

class Customer {

  public:

    void setAddress(const Address& address);

};

 

17. Use Verbs to Name Functions

class Account {

  public:

    void withdraw(int amount);

    void deposit(int amount);

};

 

18. Use "is", "set", and "get" to Name Accessor and Mutator Functions

-Variable and Parameter Names

19. Use "lowerCamelCase" for Variable and Function Parameter Names

class Customer {

  public:

    Address setAddress(const Address& address);

  private:

    Address address_;

};

 

Address Customer::setAddress(Address address) {

  Address oldAddress = address_;

  address_ = address;

  return oldAddress;

}

 

20. Use Nouns to Name Variables

class Customer {

  private:

    Address billingAddress_;

    Address shippingAddress_;

    Phone daytimePhone_;

    Orders openOrders_;

};

 

21. Add a Prefix or Suffix to Member Variable Names to Distinguish Them from Other Variables

22. Name All Function Parameters

如果函数不适用参数,使用 static_cast<>将变量转为void类型,避免“未使用变量”警告。

如果函数没有参数,放入一个关键词void。

class MyClass {

  public:

    MyClass(void);        //good

    MyClass(int);         //bad

    void method();      //Inconsistent

};

 

MyClass::MyClass(float meaningfulName) {

  static_cast<void>(meaningfulName);  //good

  // ...

}

 

23. Use "other" for Parameter Names in Copy Constructors and Assignment

class A {

   A(const A& other);

   A& operator=(const A& other);

};

 

24. Given Function Parameters the Same Name as the Member Variables You Assigned Them to

 

-General

25. Use meaningful Names

if (age < RetirementAge) {

   yearsToRetirement = RetirementAge - age;

}

else {

  yearsToRetirement = 0;

}

 

for (size_t i = 0; i < numberOfStudents; ++i) {

  enrollStudent(i);

}

 

26. Use Familiar Names

27. Avoid the Use of Digits within Names

避免用数字区分名字,比如str1, str2. 如果对确定意义有帮助,可以使用, 比如 Utf16Encoding 。

28. Avoid Excessively Long Names

或许可能类在完成多余多功能,重新拆分或许是更好的办法。

29. Join the Vowel Generation - Use Complete Words

vowel元音, 不要试着去除元音来缩短名字,会降低可读性,否则看看是否原有名字恰当。

class Msg {                                        //bad

   void appndSig(Msg msg, string sig);  //bad

};

 

class Message {

  void appendingSignature(Message message, string Signature);

  void setSignalMask(int mask) const;

}

 

30. Use "lowerCamelCase" for Abbreviations

如果简写出现在类型或常量里,仅仅第一个字母大写

XMLString > XmlString

loadXMLDocument > loadXmlDocument()

但对预定义常量不适用

#define XML_DOCUMENT = "text/XML";

对函数、变量和参数的起始处不使用,因为必须小写。

Document xmlDocument;

 

31. Do Not Use Case to Differentiate Names

编译器可以区分,人容易混淆。

 

Document Coventions

32. Document Your Software Interface for Those Who Must Use it

programming contract between a client and a supplier of a service

33. Document Your Implementation for Those Who Must Maintain It

时刻记得可能不熟悉你代码的人,要读和理解它。

34. Keep Your Comments and Code Synchronized

35. Embed Application Program Interface (API) Reference Documentation in Your Source Code


2011.11.04 31-50

36. Generate API Reference Documentation Directly from the Source Code

      http://www.literatureprogramming.com/    doxygen

37. Document All Significant Software Elements

38. Document Software Elements as Early as Possible

39. Use Block Comments to Describe the Programming Interface

40. Use One-Line Comments to Explain Implementation Details

41. use a sigle consistent format and organization for all documentation comments

42. provide a summary description of every declared element

43. document the interface exposed by every function

/*

*...

* Parameters:

*   Probe the probe number to read

* Returns: the current temperature, in Celsius

* Throws: InstrumentationException if hte

  *             probe does not repond.

  * ...

  */

44. document thread synchronization requirements

45. provide examples to illustrate common and proper usage

46. document important preconditions, postconditions, and invariant conditions

47. document known defects and deficiencies

48 use the active voice to describe actors and passive voice to describe actions

49. use this rathter than the when referring to instances of the current class

/**

* Returns the string value of this object.

*/

std::string toString();

50. explain why the code does what it does

51. avoid the use of end-line comments

52. label closing braces in highly nested control structures

53. add a "fall through" comment between two case labels if no break statement separates those labels

54. use keywords to mark pending work, unresolved issues, defects, and bug fixes

// **FIX** #1234 - Added code to flush buffer.

 

// :UNRESOLVED: Greg B., 2002/11/19

// This code does not handle the case where

// the input overflows the internal buffer!!

while (everMoreInput) {

  ...

}

 

Programming Principles

-Engineering

55. do not be afraid to do engineering

56. choose simplicity over elegance

57. don not use a feature of C++ just because it is there

58. recognizae the cost of reuse

59. program by contract

check precoditons and post conditions by assertion in appropriate public methods. check preconditions at the beginning of a method, before any other code is executed, and check postcoditions at the end of a method before the method returns.

Design Patterns: Elements of Reusable Object-Oriented Software

-Class Design

60. keep classes simple

61. define subsclass so they may be used anywhere their superclasses may be used

The Liskov Substitution Principle

     Methods that use references to superclasses must be able to use objects of subclasses without knowing it.

The Open-CLosed Principle

     Software entities, i.e. calsses, modules, functions, and so forth, should be open for extension, but closed for modification.

 

 2011-11-05  51-70 14:45-16:34

62. use inheritance for “is a” relationships and containment for “has a” relationships

63. avoid multiple inheritance

 

-Thread Safety and Cocurrency

two synchronization techniques: mutual exclusion and conditional synchronization

POSIX threads library

Microsoft WindowsTM API

64. Design for Reentrancy

65. use threads where appropriate

to simultaneously repond to many events

to provide a high level of responsiveness

to take advantage of machines with multiple processors

66. avoid unnecessary synchronization

67. do not synchronize access to code that does not change shared state

 

Programming Conventions

-preprocessor

68. use ‘#include “…”’ for collocated header files and ‘#include<…>’ for external header files

69. place preprocessor include guards in header files

----MyClass.h

#ifndef MYCLASS_H

#define MyClass_h

#endif  // MYCLASS_H

 

----- A file that uses MyClass.h

#include “MyClass.h”

 

70. use #if..#endif and #ifdef..#endif Instead of “/*…*/”comments to hide blocks of code

class MyClass {
    public:
        #ifdef DEPRECATED
            void method(void);
        #endif
	void method(size_t start, size_t end);
};

void
MyClass::method(size_t start, size_t end)
    #ifdef ECHO_CHECK
        std::cout << static_cast<int>(start);
        std::cout << ",";
        std::cout << static_cast<int>(end);
        std::cout << std::endl;
    #endif

    if TBD {
    anotherMethod(start, end);
    #endif  }
    
    #if TBD
    yetAnotherMethod(void);
    #endif
}

71. use macros sparingly

Bjarne Stroustrup wirtes:

“The first rule about macros is: do not use them if you do not have to. Almost every macro demonstrates a flaw in the programming language, in the program, or in the programmer.”

Instead of macros, prefer constants, enumerations, inline functions, or templates. The primary practical uses for macros in C++ are conditional compilation and special cases where you need compilation unit filenames and line numbers.

again <<The Design and Evolution of C++>>

 

72. Add a semicolon after every statement expression Macro

void foo() {
    TRACE_BEGIN
    MACRO_FUNCTION(foo)    //Bad
}

void bar() {
    TRACE_BEGIN;
    MACRO_FUNCTION(bar);    //GOOD
}

73. use macros to capture the current file name and line number

template <typename ReturnType, typename Arg0Type, typename Val0Type>
ReturnType traceCall(ReturnType (*function)(Arg0Type),
                                 Val0Type arg0,
                                 const char* file,
                                 int line) {
    //Do Trace...
    std::cout << file << ":" << line << std::endl;
    return (*function)(arg0);
}

// Use macro to capture the file name and line number
#define TRACE_CALL_1ARG(function, arg0) \
    traceCall(function, arg0, __FILE__, __LINE__);

void foo(const char* message);
int bar(const char* message);

void doSomething() {
    TRACE_CALL_1ARG(foo, "Message");
    int result = TRACE_CALL_1ARG(BAR, "Message");
}

74. do not use “#define” to define constants-declare static const variables instead

#define PI 3.1415926	// Bad
static const double Pi = 3.1415926;	//Good 
-Declarations
75. Use portable types for portable code
Linux and Unix platforms “inttypes.h” 
76. use typedefs to simplify complicated type expressions
class Good {
    public:
        typedef int (*Callback)(int, const char*);
        typedef pair<Date, const char*> logEntry;
        typedef const vector<LogEntry> Log;

        void setCallback(Callback callback);
	void addToLog(const Log& log);
	const Log& getLog() const;
    private:
	Callback callback_;
	Log log_;
};
 

77.create a zero-valued enumerator to indicate an uninitialized, invalid, unspecified, or default state

enum Color {
    None,
    Red,
    Green,
    Blue
};

enum Color {
    Default,
    Red,
    Green,
    Blue
};

78. do not define enumerations using macros or integer constants

 

-Scoping

79. declare enumberations with a Namespace or Class

80. declare global functions, variables, or constants as static members of a class

81. declare for-loop iteration variables inside of for statements

 

-Functions and Methods

82. use an enumberation instead of a boolean to improve readability

class Caller {
    public:
        typedef void (*Callback)() Callback;
    enum Duaration {
        DeleteAfterCall,
	KeepAfterCall
    };

void addCallback(Callback callback, bool keepAfter);
void addCallback(Callback callback, Duration duration);
};

void
callback() {
    // ...
}

void
doSomething() {
    Caller caller;

    // But what did 'true' mean?
    caller.addCallback(callback, true);

    // The choice of behavior is obvious in this call
    caller.addCallback(callback, Caller::KeepAfterCall);
}
83. use an object pointer instead of a reference if a function stores a reference or pointer to the object
a non-const reference parameter indicates that your function may perform non-const operations on the object but does not indicate whether that function saves a reference to the object
class ListElement {
  public:
    ListElement(ListElement& prev,
		ListElement& next);

  private:
    ListElement* prev_;
    ListElement* next_;
};
    
ListElement::ListElement(ListElement& prev,
			 ListElement& next)
    : prev_(&prev), next_(&next) {
}

class ListElement {
  public:
    ListElement(ListElement* prev,
		ListElement* next);

  private:
    ListElement* prev_;
    ListElement* next_;
};
    
ListElement::ListElement(ListElement* prev,
			 ListElement* next)
    : prev_(prev), next_(next) {
}
 
84. Accept Objects by reference and primitive or pointer types value
void importantFunction(const BigObject& param);

85. use a const char* for narrow character string parameters

86. pass enumerator values, not integer constants

87.do not use void* in a public interface

88.use inline functions instead of macros

89.inline only the simplest functions

usually not more than 3-4 lines, inline qualifier is just a hint to the compiler, one that the compiler is not required to heed

90.factor functions to allow inlining of trivial cases

class Foo {
     public:
         inline void doSomething(void);
     protected:
         void doSomethingComplicated(void);
     private:
         enum State {
              Simple,
	      Complicated
         } state_;
};

inline void
Foo::doSomething(void) {
    if (state == simple) {
        //inline the code for trivial case here...
    }
    else {
        // call a non-inline function for
        // complicated case
        doSomethingComplicated();
    }
}
	

-Classes

91. define small classes and small methods

92. build fundamental classes from standard types

93. avoid the use of virtual base classes in user-extensible class hierarchies

94. declare the access level of all members

95. declare all member variables private

class Superclass {
	protected:
		Foo foo_;	// Currently referenced by value
};

class Subclass : public Superclass {
	public:
		void method(void);
};

void Subclass::method(void) {
	foo_.fooMethod();	//incorrect!
}

class Superclass {
	protected:
		Foo& getFoo(void);
	private:	//Right!
		Foo foo_;
};

Foo*

Superclass::getFoo(void) {
	return &foo_;
}

class Subclass : public Superclass {
	public:
		void method(void);
};

void Subclass::method(void) {
	getFoo()->fooMethod();
}
 

96. avoid the use of friend declarations

class A {
	public:
		int compareTo(const A& other) const;
};

bool operator==(const A& lhs, const A&rhs) {
	return lsh.compareTo(rhs)==0;	//Good!
}

 

another scenario- handle-body pattern 

book: design pattern

 

-Class Members

97. declare an explicit default constructor for added clarity

98. always declare a copy constructor, assignment operator, and destructor if the class can be instantiated

99. always implement a virtual destructor if your class may be subclassed

class Base {
	public:
		Base(void);
		~Base();			//Bad
};

class Derived : public Base {
	public:
		Derived(void);
		~Derived();			//Bad
};

Derived::Derived(void) {
	//allocate a resource
}

Derived::~Derived() {
	// free that resource
}

void cleanup(Base* b) {
	delete b;
}

class Base {
	public:
		Base(void);
		virtual ~Base();			//Good
};

class Derived : public Base {
	public:
		Derived(void);
		virtual ~Derived();			//Good
};

100. Make Constructors Protected to Prohibit Direct Instantiation

To prohibit direct instantiation of an abstract class, you should only allow protected access to all of its consturctors.

101. Make Constructors Private to Prohibit Derivation

To prohibit derivation from a “closed” class, declare all constructors as private. Use a public static method to instantiate the class. This method as a “factory” for the class.

#include <memory>

class ClosedClass {
	public:
		//use this function to construct an instance
		static
		std::auto_ptr<ClosedClass> makeClosedClass();
	private:
		ClosedClass(void);
		ClosedClass(const ClosedClass& other);
};

ClosedClass::ClosedClass(void) {
		//...
}

std::auto_ptr<ClosedClass>
ClosedClass::makeClosedClass(void) {
	return std::auto_ptr<ClosedClass>
			(new ClosedClass());
}

// Illegal! This derivation cannot be instantiated!
class Derived : public ClosedClass {
	// No access to ClosedClass constructors!
	Derived(void);
};

 

102. declare a private operator new() to prohibit dynamic allocation

Resource Acquisition is Initialization RAII

class Mutex {
	public: 
		Mutex(void);
		void acquire(void);
		void release(void);
		class Lock {
			public: 
				Lock(Mutex& mutex);
				~Lock();
			private:
				// prohibit dynamic allocation 
				// Note: No implementation is required!
				void* operator new (size_t);
				void* operator new[] (size_t);
				Mutex& mutex_;
		};
};

Mutex::Lock::Lock(Mutex& mutex)
: mutex_(mutex) {
	mutex_.acquire();
}

Mutex::Lock::~Lock() {
	mutex_.release();
}

class A {
	public:
		void bad(void);
		void good(void);
	private:
		Mutex mutex_;
};

void A::bad() {
	// If you define a private operator new(0, the folowing
	// will produce a compile-time error!
	Mutex::Lock* lock = new Mutex::Lock(mutex_);
	//...
	if (error) throw std::exception();
	//...
	delete lock;	//Bad! Throw will skip this!
}

vid A::good() {
	Mutex::Lock lock(mutex_); //Good!
	//...
	if(error) throw std::exception();
	// ...
	// lock is always destroyed and mutex released
}

103. declare a protected or private destructor to prohibit static or automatic allocation

a compiler does not allow you to create a static or automatic instance of a class that does not have a public destructor

#include <memory>

class HeapOnly {
public:
	HeapOnly(void);
private:
	// Give auto_ptr<> access to destructor
	friend class std::auto_ptr<HeapOnly>;
	// Prohibit static or automatic allocation
	~HeapOnly();	//Dynamic allocation only
};

HeapOnly s;	// Oops! compilation error
std::auto_ptr<HeapOnly> p(new HeapOnly());	//OK

class Handle;	// Forward reference

class Body {	// Base class for body classes
	friend class Handle;
	public:
		Body(void);
	protected:
		virtual ~Body();
	private:
		void addReference(void);
		void removeReference(void);
		size_t count_;
};

Body::Body()
: count_(0) {
}

Body::~Body() {
}

void
Body::addReference(void) {
	++count_;
}

void 
Body::removeReference(void) {
	if (0 == --count_) delete this;
}

class Handle {
	public:
		Handle(Body* body);
		Handle(const Handle& other);
		Handle & operator=(const Handle& other);
		~Handle();
	private:
		Body* body_;
};

Handle::Handle(Body *body)
: body_(body) {
	body_->addReference();
}

Handle::Handle(const Handle& handle)
:body_(handle.body_) {
	body_->addReference();
}

Handle& Handle::operator=(const Handle& other) {
	Body * temp = body_;
	other.body_->addReference();
	body_= other.body_;
	return *this;
}

Handle::~Handle() {
	body_->removeReference();
}

// Body b;			// Not allowed!
Handle first(new Body());	//OK!
Handle second(first);		//Makes a copy

104. declare single-parameter constructor as explicit to avoid unexpected type conversions

MyClass c = 42;

class MyClass {
	public:
		explicit MyClass(int n);
};105. use default arguments to reduce the number of constructorsclass Brush {
	public:
		Brush(void);
		Brush(Color c);
		Brush(Color c, Texture t);
};

Brush(Color c = Color.Black; Texture t = Texture.Solid);

106. do not overload non-virtual methods in subclasses

#include <iostream>

class B {
	public;
		void foo(double d);
};

void
B::food(dpuble d) {
	std::cout << "B::foo(" << d << ")";
	std::cout << std::endl;
}

class D : public B {
	public:
		void foo(int i);
};

void 
D::foo (int i) {
	std::cout << "D::foo(" << i << ")";
	std::cout << std::endl;
}

int main () {
	D d;
	d.foo(1.5);
	return 0;
}

D::foo(1)

d.B::foo(1.5);

105.

 

转载于:https://www.cnblogs.com/wonderlily/archive/2011/11/03/2233798.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值