把C++回顾了,对一些细节总结下:
C++3个特性:抽象、继承、多态
虚函数:
- 作用: 定义一个指向基类的指针,用一个派生类指针对其赋值,当调用派生类与基类一模一样的成员函数(参数、函数名)
- 当基类中的此函数没有声明为virtual函数时,只能调用基类中的函数
- 反之,如若基类中的此函数virtual ,则调用派生类中的函数
- 实现:当基类中有任意虚函数时,在基类中维护一个虚函数函数表指针,即比原先的类声明多了四字节,当派生类中声明了相同的函数时,就用自己的函数地址代替原先的函数地址,通过引用或指针调用虚函数时,则到虚函数表中,取出相应的函数入口地址执行。
- 典型应用: 当基类与派生类中都有动态申请内存空间的数据成员,当用一个派生类指针(引用)对一个基类的指针(引用)赋值时,对此指针执行delete操作,则只调用基类中的析构函数释放基类中动态申请的内存空间,造成派生类中的动态申请空间没有释放,导致内存泄露。如若,声明为virtual析构函数,则从派生类析构一直调用到基类的析构函数。(实现原理大致上也为将虚析构函数维护在一张表中,调用时,调用整张表的析构函数)。
Effective C++:
- 使用const、inline 代替中的define(有些C++不支持)
- 不要在构造函数与析构函数中调用虚数
- 原因:在派生类中的构造与析构函数中调用析构函数时,先构造基类,此时派生类成员还未初始化,C++不允许发生这种情况,所以调用基类中的虚函数。反过来,派生类先析构,其完成后,其数据成员没有被定义,所以基类中的只调用自己的虚函数
- 解决方法:在派生类的构造函数中,在初始化列表中对基类传送信息?
- 在operator=等运算符重载中,返回*this指针(与一些STL实现一致)
- 在copy构造与copy assignment中对自己和基类的所有成员赋值。在这两者中最好不要相互调用 (如果两者代码相同的很多,则再定义一个函数,供那两个调用)
- 对于数据成员中有const、引用或者继承或包含的类没有默认构造函数,则必须自己写构造函数 。对于有指针成员和上述情况的,也必须自己写copy构造与copy assignment函数
关于函数参数与函数返回:
- 给函数传递一个不是引用class对象,会调用一次拷贝构造函数
- 函数返回一个局部class对象时,会调用拷贝构造函数
- 对于运算符重载时,如加号重载,会产生一些隐形的转换:如下
class CComplex//类实现
{
private:
double dReal;
double dImaginary;
public:
CComplex(double Real = 4.0,double Imaginary = 3):
dReal(Real),dImaginary(Imaginary)
{
cout<<"In default CComplex."<<endl;
}
CComplex operator+(CComplex obCComlex);
CComplex(CComplex & obComplexTem)
{
cout<<"In copy constructe"<<endl;
dReal = obComplexTem.dReal;
dImaginary = obComplexTem.dImaginary;
}
CComplex& operator=(CComplex &obCComlex);
~CComplex()
{
}
void print(void)
{
cout<<dReal<<'+'<<dImaginary<<endl;
}
};
#include <iostream>
#include "Complex.h"
using namespace std;
int main(int argc,char*argv[])
{
CComplex CC1;
CC1.print();
CComplex CC2(3,3);
CC2.print();
CC1 = CC2;
CComplex CC3;
CC3.print();
CC3 = CC1 + CC2;
CComplex CC4;
CC4 = CC1 + 4.0;
CC4.print();
return 0;
}
CComplex CComplex::operator+(CComplex obCComlex)
{
CComplex obCC;
cout<<"In operator+"<<endl;
obCC.dReal = obCComlex.dReal + dReal;
obCC.dImaginary = obCComlex.dImaginary + dImaginary;
return obCC;
}
CComplex(double Real = 4.0,double Imaginary = 3):
dReal(Real),dImaginary(Imaginary)
{
cout<<"In default CComplex."<<endl;
}
如CC4 = CC1 + 4.0;
代码,编译运行时正常的,因为class的有一个构造函数需要0或1或2double类型的参数,执行上面的代码时,会调用默认的构造函数(4,3)的临时对象。- 如果构造函数前加上explicit关键字(不能进行隐形的从double转成CComplex) 或则 加号的运算符重载需要一个CComplex引用类型参数,则编译不通过,提示no operator found which takes a right-hand operand of type 'double'