.
条款01 视C++为一个语言联邦
C++主要分为以下四个方面:
1、C
2、OOC++ 即面向对象:例如封装、继承、多态
3、Template C++ 即泛型编程
4、STL STL有自己的办事准则 当你使用时必须要遵守它的规定
条款02 尽量以const、enum、inline替换#define
即宁可以编译器替换替换预处理器
#define A 1.652
其实记号A可能根本就没被编译器看见,也许编译器开始处理源码时它已经被预处理器移走了。
解决方法为采用常量替换上述宏
const double A=1.652;
//两种特殊情况值得讨论
//1.定义常量指针 由于常量定义通常放入头文件 以便被包含 所以有必要将指针定义为常量
//其实是指 指针指向的对象是不变的 如
const char* const name="LBH";
//const修饰离自己近的 第一个const修饰char 代表是一个常量字符
//第二个const修饰* 表示指针指向的方向是固定的
//2.class专属常量 将常量作用域限制在class内 必须让它成为class一个成员(private)
//若确保只有一个实体 还得加上static
class GamePlayer{
private:
static const int Num=5;
int score[Num];
};
//类似的有enum枚举
class GamePlayer{
private:
enum{ Num=5; }
int score[Num];
};
//enum类似于#define 如不能被取地址
#define一旦被定义 再之后编译过程中将一直有效 所以不能定义class常量,也没用封装性。
对于类似于函数的宏,最好用inline代替#define
inline:用于代码少、简单的函数
在 c/c++ 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。
//无论何时写出这种宏 必须要加上小括号
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
//inline代替 利用模板 具有通用性
template<typename T>
inline void callWithMax(const T&,const T&){
f(a>b?a:b);
}
条款03:尽可能使用const
const修饰原则: const永远修饰离自己近的!!!
char greeting[]="hello";
char* p=greeting;
const char* p=greeting;//non-const pointer,const data
char* const p=greeting;//const pointer,non-const data
除非确实有需要要改动参数,否则将它们声明为const
const成员函数:将const实施于成员函数的目的,是为了确认该函数可作用于const对象身上
class Screen {
public:
char get() const;//函数后加上const
};
//若将成员成员函数声明为const,则该函数不允许修改类的数据成员 即无法赋值操作
class Screen {
public:
int ok() const {return _cursor; }
int error(intival) const { _cursor = ival; }
};
//当然 如果加上mutable 释放掉const函数的non-static,则可以改变
class A{
public:
int change()const{
i=2;//可以的
return i;
}
private:
mutable int i;
}
值得注意的是,把一个成员函数声明为const可以保证这个成员函数不修改数据成员,但是,如果据成员是指针,则const成员函数并不能保证不修改指针指向的对象,编译器不会把这种修改检测为错误。例如:
class Name {
public:
void setName(const string &s) const;
private:
char *m_sName;
};
void setName(const string &s) const {
m_sName = s.c_str(); // 错误!不能修改m_sName;
for (int i = 0; i < s.size(); ++i)
m_sName[i] = s[i]; // 不好的风格,但不是错误的
}
要注意:当const和non-const成员函数有着实际等价实现的时候,可以令non-const版本调用const版本避免代码重复,但是需要const_cast和static_const等完成转换。别用const调non-const!
class TextBlock{
public:
const char& operator[](std::size_t position)const{
return text[position];
}
char& operator[](std::size_t position){
return const_cast<char&>(static_cast<const TextBlock&>(*this)[position])
//*this从原始的TextBlock&转换成const TextBlock,才能调用const版本的operator函数
//const_char<char&>则是从const operator的返回值中移除const
}
private:
std::string text;
}
条款04:确定对象被使用前先被初始化
永远在使用对象之前将他初始化,对于内置类型,必须手工完成,对于其他东西,初始化责任落在构造函数上,确保每个构造函数都将对象的每个成员初始化。
用初始化列表代替赋值:如果成员变量是const或者&,则必须需要初值,而不能被赋值,所以:总是采用初始化列表,往往比赋值高效。
初始化列表时最好按照形参顺序绑定,方便阅读!
class A{
public:
A(const string& name,const string& address)
private:
string thename;
string theaddress;
}
A::A(const string& name,const string& address){
name=thename;
address=theaddress;//这些都为赋值 而非初始化
}
//直接初始化
A::A(const string& name,const string& address):thename(name),theaddress(address){}
为免除“跨编译单元之初始化次序”问题,用local static替换non-static local
通俗来说, local对象 ——> 有域限制的对象, 比如一个命名空间内,一个块内等等)受作用域限制)
non-local 对象 ——> main函数结束才结束,比如全局变量 不受作用域限制)
static 存在与 date区或者bbs区的变量, 不是 stack变量, heap变量等等。(知道程序完整结束才被收回)
综上所述, non-local static 变量就是 不受作用域限制 且 为static 的对象
所以当两个non-local static对象存在调用关系时,会引发问题。例如b的调用要用到a,此时a可能还未被初始化。
所以将其换位local static是必要的
例如:1.cpp
class eggs
{
public: void eggs()
{
chicken().make_egg();
}
}
eggs& egg()
{
static egg a
return a;
}
2.cpp
class hen
{
Public: size_t make_eggs( );
}
hen& chicken()
{
static hen ji;
return ji;
}
#include "1.cpp"
#include "2.cpp"
int main(){
eggs egg();//合法了
}