C++ 经验条款

jopen 10年前

C++经验谈:

一、绝不让构造函数称为虚函数:

         从最简单的思想来看,C++对象模型中是根据虚函数表来管理虚函数的,那么在调用虚函数时,需要找到虚函数表,在对象没有创建成功时是没有虚函数表指针的,构造函数就是构造对象的,在对象没有创建成功之前来寻找虚函数表是不合理的。

         一般情况下,编译器会为每个类生成一个公有的默认构造函数,但是有两种特殊情况例外:

         一个类显示声明了构造函数,这种情况下编译器不会生成公有默认构造函数,如果程序需要一个默认构造函数,则需要程序员显示地提供。

         一个类声明了一个非Public的构造函数,编译器不会生成公有的默认构造函数。

         如果类有继承,那么首先完成基类的构造,再完成继承类的构造,在析构时,顺序相反。

         如果基类有virtual函数,那么在继承类的构造中还需要有虚函数表的创建。

 

         关于拷贝构造函数的调用:

         当类的一个对象去初始化该类的另一个对象时,拷贝构造函数会被调用。

         如果函数的形参是类的对象,调用函数进行形参和实参的结合时,拷贝构造函数会被调用。

         如果函数的返回值是类对象,函数调用完成返回时,拷贝构造函数会被调用。

 

避免在构造/析构函数中调用虚函数:

         基类的构造函数会在派生类构造函数执行之前被调用,所以当基类构造函数运行时,派生类的数据成员都没有进行初始化。

         虚函数的调用机制完全由基类控制,所以如果基类没有完成构造,虚函数的调用机制(既虚函数表)没有完成初始化,通过虚函数实现的所有期望操作都将失败。

 

Class对象大小与什么有关系:

         如果一个class中无任何数据成员,那class的大小时1.

         Class中对象的大小仅与对象的数据成员大小有关系,而与对象的函数成员无任何关系。

         Class对象非静态数据成员占用内存大小会影响对象大小

         Class对象采用的内存对齐策略会影响类对象大小

         Class对象中数据成员不会影响对象大小

         Class中virtual函数会影响类对象大小,因为virtual-talbe的原因虚函数占4字节空间

关于虚继承影响对象大小:

         由于涉及虚函数表和虚基表,会同时增加一个(剁成继承下对应多个)vfptr指针指向虚函数表vftable和一个vbptr指针指向虚基表 vbtable,这两者所占的空间大小为:8(或者8乘以多继承时父类的个数),记住有各自的虚函数表指针和虚基类指针,都共享虚基类指针即可。

         类对象的大小影响因素不包括静态数据成员,因为累中的静态数据成员分布于全局存储区域,不占用类对象的空间。

 

运算符重载:

         不要重载&&和||运算符,因为重载&&和||,会导致&&和||失去简短求值功能。

         如果一个重载操作符是类成员,那么只有当和它一起被使用的左操作数是该类对象时,它才会被调用。如果该操作符的左操作数必须是其他类型,那么重载操作符必须是命名空间成员。

 

Public private protected三种继承方法:

         Public继承时最常用的继承机制,public继承时子类具有最大的权限,公有继承的特点是基类公有成员和保护成员作为派生类的成员时,他们都保持原有的状态,而基类的私有成员仍然是私有的。

         Protected是一种比较有特点的继承机制。保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元函数访问,基类的私有成员仍然是私有的。

         Private是一种比较少见的继承方法,私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。

 

         单继承在构造时,首先是构造基类,然后是进程自身的构造。如果基类仍然存在上层基类,则首先进行上层基类的构造,然后再构造基类,析构的顺序和构造的顺序是严格按照构造顺序的逆序进行的。

 

         不要重新定义继承而来的非虚函数,以防止产生函数覆盖现象。

 

         带默认参数的virtual函数实现时,虚函数采用动态绑定,默认参数采用静态绑定。在基类虚函数中声明的默认参数,即使派生类中重新指定了默认参数,在多态调用时,依然采用基类虚函数中的默认参数

 

         不要试图尝试重载虚函数。

 

 传值调用、引用传值、指针传值三种方式的区别和联系:

1、  值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即使堆栈中开辟内存空间以存放由主调函数传进来的实参值,从而成为实参的一个副本,值传递的特点是被调函数对形式参数的任何操作都作为局部变量进行,不会影响主调函数的实参变量的值,如果想通过传值方式实现两数据的交换,这种方法不可取。

2、  引用传递过程中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调用函数对形参的任何操作都被处理成指针间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做任何操作都影响主调函数中的实参变量。

3、  指针传递时传值调用的特例,即传的值为主调函数变量的地址。被调函数的形式参数同样在堆栈中为局部变量开辟了内空间,被调函数对局部变量的任何操作都会作用在主调函数变量的地址之上。因此被调函数通过局部形参所做的任何操作都会影响主调函数中的实参变量。

 传值调用、引用传值、指针传值三种方式的区别和联系:

1、  值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即使堆栈中开辟内存空间以存放由主调函数传进来的实参值,从而成为实参的一个副本,值传递的特点是被调函数对形式参数的任何操作都作为局部变量进行,不会影响主调函数的实参变量的值,如果想通过传值方式实现两数据的交换,这种方法不可取。

2、  引用传递过程中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调用函数对形参的任何操作都被处理成指针间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做任何操作都影响主调函数中的实参变量。

3、  指针传递时传值调用的特例,即传的值为主调函数变量的地址。被调函数的形式参数同样在堆栈中为局部变量开辟了内空间,被调函数对局部变量的任何操作都会作用在主调函数变量的地址之上。因此被调函数通过局部形参所做的任何操作都会影响主调函数中的实参变量。

来自:http://blog.csdn.net/yusiguyuan/article/details/41620761