您的位置:首页 > 编程语言 > C语言/C++

Effective C++ 第一章——让自己习惯C++

2019-02-01 15:55 633 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/qq_38816219/article/details/86736912

Effective C++ 条款1——让自己习惯C++

条款1——视C++为一个语言联邦

C++不再是以前的C with class,而加入了很多新的特性,如Exception(异常)template(模板)STL。C++目前包含多重范型编程语言,支持过程形式(procedural)面向对象形式(object-oriented)函数形式(functional)泛型形式(generic)元编程形式(metaprogramming)**等。
C++可以看成四个次语言组成:

  1. C:C++以C为基础,;
  2. Object-Oriented C++: C语言加上了class、封装、继承、多态、虚函数;
  3. Template C++: 泛型编程的部分;
  4. STL:一个template库,容器、迭代器、算法和函数对象;

C++是一个由四个次语言组成的联邦政府、每个次语言都有自己的规约。
请记住:C++高效编程守则视状况而变化,取决于你使用C++的那一部分。

条款2——尽量以const,enum,inline替换#define

1.用const提换#define

使用#define 宏替换时可能不被编译器处理,#define不被认为是语言的一部分;#define不具有封装性。

#define ASPECT_RATIO 1.653
这种宏定义用常量
const double Aspectratio = 1.653;

以const常量替换#define时,两个特殊情况:
1)定义常量指针:要在头文件中定义一个常量的char*-based字符串,必须写const两次

const char* const authorName = "Scott Meyers";

2)class专属常量:为将常量的作用域限制在类内,需要将其作为一个类的成员,而且要确保此常量至多只有一个实体,需要将其成为一个static成员;

class GamePlayer
{
private:
static const int NumTurns = 5;
int scores[NumTurns];
};

2.enum代替#define

当编译器不支持在“in class初值设定”,可以采用“the enum hack”补偿的方法 ;enum绝对不会导致非必要的内存分配;

class GamePlayer
{
private:
enum { NumTurns= 5 };  //“the enum hack” --令NumTurns
int scores[NumTurns];  //成为5的一个记号
};

3.inline代替#define

#define常常被用来实现宏(macros),宏可以提高效率,存在不可预料的事情以及宏中所有实参加上括号的麻烦。
如下例:

//以a和b中较大的数调用函数f
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))//必须为宏中所有实参加上括号

int a =5,b=0;
CALL_WITH_MAX(++a,b);//a被累加两次
CALL_WITH_MAX(++a,b+10);//a被累加一次

用inline来提升效率可以替代#define定义的宏;

template<typename T>
inline void callWithMax(const T& a,const T& b)
{
f( a > b ? a : b );
}

请记住:

  • 对于单纯常量,最好以const对象或enum量替换#define;
  • 对于形似函数的宏(macros),最好改用inline函数替换#define;


条款3——尽可能使用const

const:指定一个约束语义,指定一个不允许被改动的对象**;用于修饰global或namespace作用域中的常量、修饰文件、函数、或区块作用域中被声明为static对象,修饰class内部的static和non-static成员变量,还可以修饰指针本身指针所指物,或者两者都是都不是)const。
1.const修饰指针:

char greeting[]="holle";
char *p = greeting;  //non-const指针,non-const数据
const char *p = greeting;//non-const指针,const数据
char* const p = greeting;//const指针,non-const数据
const char* const p = greeting;//const指针,const数据

const出现在左边,表示所指物为常量;const出现在右边,表示指针本身为常量;const出现在*两边,表示所指物和指针本身都为常量。

2.const修饰函数:可以修饰返回值、各参数、成员函数本身;

  1. 令函数返回一个常量值,可以降低用户错误而造成的意外,又不至于放弃安全性和高效性。
class Rational {...};
const Rational operator* (const Rational& lhs, const Rational& rhs);
//如果这种情况下不返回const量的话,可能有的程序员写出这样的语句
Rational a, b, c;
( a * b ) = c;//错误的
  1. const参数:const修饰参数时函数内将不能改变参数;

  2. const成员函数:修饰成员函数使成员函数不改变调用的对象;使“操作const对象成为可能”;提升c++程序效率的根本办法就是按照pass by reference-to-const方式传递;
    有时候存在类中存在指针时,当我们不改变指针,但是改变指针所指向的值时,编译器认为他是bitwise constness;
    mutable可以改变non-static成员变量的bitwise constness约束,这样可以在const成员函数中修改 mutable变量修饰的成员变量;

  3. 在const和non-const成员函数中避免重复:用const成员函数可以实现其non-const孪生函数,需要用到const_cast类型转换;

请记住:

  • 将某些函数声明为const,可以帮助编译器侦测出错误用法。const可以被用于任何作用域内的对象、函数参数、函数返回类型、成员函数本体;
  • 编译器强制实施bitwise constness,但你编写程序时应该使用概念上的常量性;
  • 当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复;


条款4——确定对象被使用前已先被初始化

1 对象必须初始化

读取为初始化的值会导致不明确的行为。
最佳的处理办法是:在使用对象之前将它初始化。

  • 对于内置类型,手动完成初始化;
int x = 0;//对int手工初始化
const char * text = "A C-style string";//对指针手工初始化

double d;
std::cin>>d;//以 input stream 方式初始化double值
  • 内置类型以外的任何其他东西,确保构造函数将对象的每一个成员初始化;
    c++中对象的成员变量的初始化动作发生在进入构造函数体本体之前,最好的办法是使用成员初始化列表member initialization list。
#include<string>
#include<list>
class PhoneNumber {...};
class ABEntry{             //ABEntry = "Address Book Entry"
public:
ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>&
phones);
private:
std::string theName;
std::string theAddress;
std::list<PhoneNumber> thePhones;
int numTimesConsulted;
};
ABEntry::ABEntry(const std::string& name,const std::string& address,
const std::list<PhoneNumber>& phones)
{
theName = name;     //赋值语句
theAddress = address;
thePhones = phones;
numTimesConsulted = 0;
}

上述程序中的构造函数中函数体中的语句

theName = name;...numTimesConsulted = 0;
都不是被初始化,而是被赋值。

ABEntry::ABEntry(const std::string& name,const std::string& address,
const std::list<PhoneNumber>& phones)
:theName(name),theAddress(address),thePhones(phones),numTimesConsulted(0)
{}

规定:总是在成员初始化列表中列出所有成员变量,以免还得记住哪些成员变量可以无需初值。
成员变量是const或references,一定要使用初值,而不能被赋值。因此为了避免要记住成员变量何时必须在成员楚淑慧列表中初始化,何时不需要,最简单的办法就是:总是使用成员初始化列表初始化。

2 多个构造函数的初始化处理避免重复代码

许多class拥有多个构造函数,每个构造函数都有自己的成员初始化列表,多个初始化列会导致重复代码。这时可以将一些“赋值与初始化表现一样好”成员变量,改为赋值操作,并将这些赋值操作移往某个函数(通常为private),然后在各个构造函数中调用他。

3 成员初始化次序

按照class中声明的次序初始化,即使在成员初始化列表中出现的顺序不同,最好成员初始化表中列出的顺序与声明次序一致,基类(based classes)先与派生类(derived classes)初始化。

4.不同编译单元内定义的non-local static 对象的初始化次序

不同编译单元内定义的non-local static 对象的解释:

  • non-local static 对象:其寿命从被构造出来到程序结束为止,程序结束自动销毁;
  • 编译单元:产出单一目标文件的那些源码,基本上是单一源码文件加上其所含有的头文件(#include files);

以函数调用(返回一个reference指向local static对象)替换直接访问“non-local static 对象”。

请记住:

  • 为内置类型手动初始化,因为C++不保证初始化他们;
  • 构造函数最好使用成员初始化列表,而不是在构造函数中使用赋值操作;初始化列表列出的成员变量其排列次序应该和它们本身在class中声明的次序相同;
  • 为免除“跨编译单元的初始化次序”问题,请以local static 对象替换non-local static 对象。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: