Effective C++学习笔记 分类: C/C++ 2015-06-08 16:52 27人阅读 评论(0) 收藏

1、C和C++的区别

       1)从语法上来说,C是C++的一个子集,C++是在C的基础上发展而来,C++对C的兼容的;其次,C++除了C这一模块之外,还有包括:面向对象的部分(类、继承、封装、多态等),泛型编程部分,STL。

       2)从编程思想上来说,C是一种结构化的语言,它的重点在算法和数据结构上,是一种面向过程的思想,程序设计时,是从输入通过各种运算产生输出的一个过程。而C++是一种面向对象的语言,程序设计时,首先分析问题,抽象出该问题的模型,通过对对象的操作完成输出。前面说过,C是C++的一个子集,那也就是说,C++也可以面向过程,所以C++并非完全的面向对象。

2、宏定义的缺点:

      1)没有类型检查,所以不是类型安全的;2)就地展开,宏名称不进行记号表,不利于调试并导致较大的代码量;

3、类中,初始化列表中的初始化顺序是声明顺序,这一点很重要,如果顺序出错可能导致某些变量是不确定值;

4、static对象的寿命从被构造出来直到程序结束为止。

5、将成员变量设为private的重要性

      1)语法一致性,使用的都是函数,都要加上括号;2)精确的读写权利控制,读、写操作都通过函数完成;3)封装,将成员变量隐藏在函数接口的背后,可以为“所有可能的实现”提供弹性。public/protected意味着不封装,不封装就意味着不可改变,因为一旦改变所需要更改的用户代码太多。protected不比public更具封装性。

6、以非成员函数和非友元函数代替成员函数的优势:

      1)增加封装性,越多的函数可以访问class的私有变量,其封装性越低。

      2)增加可扩展性,使用非成员函数和非友员函数的便利函数,用户可以轻松扩展这一组便利函数,如,clearEveryththing()函数。当以成员函数实现clearEverything()时,因为class的定义式无法拆分,必须放在同一个头文件当中。

namespace WebBrowserStuff{
	class WebBrowser{
	public:
		...
		void clearCache();
		void clearHistory();
		void removeCookies();
		...
		void clearEverything();
		...
	};

	void clearEverything(WebBrowser& wb)
	{
		wb.clearCache();
		wb.clearHistory();
		wb.removeCookies();
	}
}

      对于一个类,用户对其的操作可能会分很多类,如打印、书签管理、用户管理等等功能,可能一个用户只需要其中某部分功能,当将一个类相关的所有操作都放在一个头文件当中时,会使所有便利函数发生编译相依关系。最好的方法是将其放在同一个命名空间但不同的头文件当中,用户使用时只需要包含相应的头文件既可。如需要扩展某些便利函数时,只需要在相应的命名空间增加函数即可。

7、当不想编译器给类创建默认copy构造函数和copy assignment构造函数时,有以下两种方法

      1)把copy构造函数和copy assignment构造函数设成私有,并不实现它们,当被调用时会产生连接错误;

      2)声明一个copy构造函数和copy assignment构造函数是私有成员的基类,当子类尝试复制操作时,由于子类无法访问基类私有成员,会产生编译错误;

      方法2将连接期的错误转移到了编译期。编译器可能产生的默认函数包括构造函数、copy构造函数、copy assignment构造函数、析构函数、另有取地址函数。默认产生的析构函数是non-virtual的,且这些函数只在需要的时候才会生成。

8、base class应该声明一个virtual析构函数,当用基类指针指向派生类对象时,在用基类指针销毁对象时,如果析构函数是non-virtual会导致部分派生类的成分没有被销毁,从而导致内存泄漏。

     如果一个类不打算作为基类,那就不应该声明virtual析构函数,因为虚函数会增加对象所占内存。

     析构函数动作方式是,最深层派生的那个class析构函数最先被调用,然后是其每一个base class的析构函数被调用。

9、析构函数不应该抛出异常,如果因抛出异常而提前返回会导致部分内存未释放,导致内存泄漏。

10、构造函数或析构函数中不能调用虚函数,因为创建派生类时,首先会构造其基类,此时对象还是基类对象,而派生类对象还未被创建,虚函数无法下降。同理,析构时,首先析构派生类对象,基类中的析构函数同样不会下降。

11、operator=应该返回一个reference to *this,并要处理两个对象是同一对象的情况。

12、如果需要对类中某个函数的所有参数(包括this指针所指的那个隐喻参数)进行类型转换,这个函数必须是non-member。只有当参数被列于参数列表中时,这个参数才是隐式转换的合格参与者,对于this指针就不能隐式转换。当函数声明为explicit时,所有参数都不能隐式转换。

13、std命名空间中不支持添加新的东西。

14、尽可能延后定义式出现的时间,因为定义了之后可能未使用,如此会带来额外的构造和析构成本。所以应尽可能延后直至要使用这个变量为止。

15、C++提供的四种新式转型

      1)const_cast通常被用来将对象的常量性转除,它也是唯一有此能力的C++-style转型操作符。

      2)dynamic_cast主要用来执行“安全向下转型”,也就是用来决定某对象是否归属继承体系中的某个转型。它是唯一无法由旧式语法执行的动作,且其效率很低。

      3)reinterpret_cast意图执行低级转型,实际动作可能取决于编译器,这也就表示它不可移植。例如将一个pointer to int转型为一个int。

      4)static_cast用来强迫隐式转换,例如将non-const对象转为const对象,或将int转为double等等。它是用的最多的转型方式。

      需要转型时,尽量使用新式转型,隐式转型不易辨识

16、不要返回指针或引用指向class的内部成员变量,会降低类的封装性。如一个函数只允许读取对象的数据,但如果返回一个引用,则可能通过该引用修改数据,相当于获得了写权限,违背有原来的意图。

       其次,返回一个指向局部对象的指针或引用也是不安全的,因为一旦函数调用结束,局部对象就会析构,从而导致指针或引用空悬。

       最后,以下这种形式也是不安全的,因为产生的临时对象在函数结束后也会析构。不是必须的时候,尽量不要使用这种形式。

GUIObject{...}
const Point* pUpperLeft=& boundingBox(*pgo)


17、异常安全函数即使发生异常也不会泄漏资源或允许任何数据结构败坏。

       异常安全函数提供下列三个保证之一:

     基本承诺:如果异常抛出,程序内任何事物仍然保持在有效状态下。

      强烈承诺:如果异常抛出,程序状态不改变。

      不抛出保证:承诺绝不招聘异常。

18、为防止资源泄漏,应该以对象管理资源,对象在构造函数中获得资源并在析构函数中释放资源。两个被经常使用的资源管理类:

     tr1:shared_ptr:可以正常复制;

     auto_ptr:不能正常复制,每次只能一个对象拥有该资源;

     但二者都是执行的delete而非delete [],所以不能用它们来管理动态数组。智能指针和其管理的底层指针并不完全等效,但可以隐式转换,也可以实现函数获取其底层指针。

new之后,放入资源管理对象之前,不应有其他操作,以防异常抛出。

19、任何接口如果要求用户必须记得做某些事情,那就存在“不正确使用”倾向。提供给用户的接口应该简单易用并想到所有可能的输入,并限制这些可能的错误输入形式。

20、inline函数的里里外外:

       1)inline函数和头文件一样的,都是就地展开,如果inline函数过大,则会增加程序的代码量,其代价可能超过函数调用,所以此时应该使用non-inline函数。

       2)inline函数是否真的是inline还得由编译器决定,编译器如果觉得不应该是inline函数,也会被当作non-inline函数处理。

       3)inline函数一般被放在头文件当中,所以,一旦inline函数被修改,所有包含该头文件的源文件都要重新编译,但如果是函数调用,则只需重新连接相应源文件即可。

       4)上面说过,inline函数一般放置在头文件中,除了显示声明为inline的函数外,定义在class内部的函数也是inline函数。

       5)当你定义了一个为空的构造或析构函数时,这个空的构造或析构函数中发生的事可能远比你想像的多,毕竟一个对象被构造或析构的时候不可能凭空出现。 

21、无论是否是virtual函数,derived class内的函数名称都会遮掩base class的名称。

       理论上,子类是不允许重写基类的non-virtual函数的。虚函数给子类提供一个函数接口和一个缺省的实现,纯虚函数给子类提供了一个接口,而non-virtual函数则是给子类提供了接口和实现。如果重写non-virtual函数则会隐藏掉基类的所有同名函数。如果需要使用被遮掩的名称在子类中可见,则可以使用using声明式或转交函数。

原文地址:https://www.cnblogs.com/zclzqbx/p/4687126.html