Effective c++笔记01

Effective c++笔记01

item1.

把C++看作一个语言联邦。
嗯…….

item2 尽量使用const,enum,inline来替换#define

  作者在书中提的原因非常简单,#define 定义的宏不利于debug。
  例如#define DOG 5,经过了预处理,程序不知道DOG的存在,他只知道有个数字5,如果在这报错,错误提示课可能会提到5,但肯定不会出现DOG,定位错误非常麻烦。
  用const来代替是一个好方法,如上则可以换成const int dog = 5;(用constexpr肯定也行。   对需要把常量局限于类中时可以用static int dog = 5;
  当然对于某些编译器在声明式上获取初值不被允许(就是你vc6),而你在编译期间又不得不需要这样一个常数,那可以使用enum hack。

 class cls{
	private:
		enum{ dog = 5 };
		int dogs[dog]
};

  对于用宏来定义函数,你完全可以用模版內联函数来代替。

	template<typename T>
  inline T MAX(const T& a,const T& b){
	 return (a > b) ? a:b;
}

  而宏定义的函数虽然可以达到同样的效果,但是会发生奇奇怪怪的事。。(例子略…..

item2 尽量使用const

  首先是很多时候都搞不清楚的指针常量和常量指针。举几个例子

 	int a =5;
	int *b =new int(5);
	const int c = 5; 
	const int* d = new int(5);//数据不能改变
	int *const e = new int(5);//指针的指向不能变
	const int *const f= new int(5);//指针指向和数据都不能变

   看起来很难,但是有个简单的记忆方法,就是看const离谁近。
   比如const int* a,const靠近int,这代表数据不能改,意味着*a = 3;//Error!int *const d. 这个const和*挨得近,代表着指针指向改不了,d = &c;//Error!
当然还有种写法int const*,它与const int*等同。
如果出现了两个const,毫无疑问,什么也改不了。

  对于STL的迭代器,它就像个T*指针,如果希望它的指向不变,只要声明一个const的迭代器(T* const),如果希望它指的东西的值不变,则需要一个从const_iterator(const T*

	std::vector<int> vec;
	……………
	const std::vector<int>::iterator tier = vec.begin();
  *iter = 10;//ok!
  ++iter;//error!  
  std::vector<int>::const_iterator cIter = vec.begin();
  *cIter = 10;//error!
	++cIter; //ok!

  让函数的返回值为const也可以避免些奇奇怪怪的错误,例如一个返回const的重载操作符 * 可以避免这样的错误(a * b) = c;//error!
  另一个点是,对于non-const的成员函数,可以重载构建一个const的成员函数。

class cls{
	public: 
    cls operator+(cls b); //for non-const object
	  cls operator+(cls b)const;//for const object
};
如果有这样一个常量的cls对象调用“+”号,那么它用的是那个const的操作符重载,而non-const的对象相反。

  如果一个成员函数是const的,那它在函数内就不能改变类中的任何属性。如果你想改变其中某个属性,请把它声明为mutable。

item4确保对象在使用前被初始化了。

   c++存在无初值对象,而没有初值(未初始化)会导致坏事发生。    所以一定要保重值的初始化。    在class中尽量使用成员初值列来进行初始化,因为c++规定成员的变量初始化发生在其进入构造函数之前。

  class Integer{
  Public:int a,b;
  Integer():a(0)//这才是真正的初始化
	{
    b = 0;//这叫赋值,不是初始化
	}
}

   最好把所有成员放入初值列中,以防遗漏,而且尽量按照声明顺序去列初值列,以免遇到奇怪的错误。    当遇到定义在不同编译单元的不同对象,初始化的顺序难以决定,这时可以使用local-static const代替static const,就是定义一个返回值为local static对象引用的函数。比如

	obj& getObj()
{
	static obj object;
	return object;
}

   这样就能确定目标一定被初始化了。

item5 C++为你默默调用编写了哪些函数

   这个是真的不看不知道,一看吓一跳,原来那些我认为理所当然的操作都是编译器默默努力的结果。
   首先,编译器会自动创建默认的构造函数和构析函数,其次,还会创建copy构造函数和copy assignment操作符。比如我有一个空类cls,但是有些操作我是一定能执行的,举例如下:

 class cls{};
 cls a,b;  //default constructor
 cls c(a); //copy constructor
 a = b;    //copy assignment
 //destructor

   但是

  1. 一旦你声明了一个构造函数,那编译器就不会再帮你生成default的构造函数了(无参构造)。
  2. 当一下条件其一编译器不会产出copy assignment(还是有copy constructor的) - [ ] 如果类中有const变量
    - [ ] 如果类中有引用
    - [ ] 当基类中有private的copy assignment时,编译器拒绝为派生类产生这函数。
      1相对好理解,2则需要些解释。
      类中有const常量,而常量一旦初始化就不允许改变。如果能改变这个常量,那就乱了套了。
      类中有引用,由于引用只能指向初始化时定的变量,一旦创建不能修改,所以也不行。
      调用派生类的copy assignment自然会调用父类的copy assignment,面对无权调用的函数,编译器也无能为力。

item6 如何拒绝编译器生成的函数

   如果你想拥有一个独一无二的类:不让它被复制一份,那么可以做如下的方法:

  1. 在类中声明私有的复制构造函数和复制符号,并且只有声明,而没有实现,这样能阻止友元函数和成员函数的调用——他们会收到一个连接错误
  2. 继承一个有以上特性的基类
END