类的三大特性
封装、继承、多态
多态的实现条件:子类重写父类的虚函数,父类的指针或引用指向子类,当调用该重写的函数时,调用子类的函数而不是父类的函数。当有多个子类时,通过不同子类调用该函数,产生不同的行为,表现为多态。
虚函数表
每一个有虚函数的类都有一个虚函数表(简称:虚表)。有虚函数的类定义的对象有一个虚函数表指针(简称:虚表指针),该虚表指针指向虚函数表,虚函数表中存储的是虚函数的地址。
多态的实现:发生在父子类之间,父类中定义virtual声明的虚函数,子类继承父类,并重写虚函数。定义的对象满足父类的指针或引用指向子类,子类对象的虚函数表中存储的虚函数地址如果是子类自己定义的新的虚函数,则虚函数的地址指向子类的函数地址;如果子类重写父类的虚函数,则虚函数地址指向子类的函数地址;如果存在子类未重写的继承自父类的虚函数,则虚函数地址指向父类的函数地址。
拷贝构造和拷贝赋值
两者的本质区别:是否有新对象的产生,拷贝构造的本质是个构造函数,会产生新的对象,而拷贝赋值只是将数值赋值给一个已经存在的对象,不会产生新的对象。
class A{
A(A & a){}//拷贝构造
};
拷贝构造函数的参数必须是引用类型,如果采用值类型,类的非指针或引用对象作为参数,会引起拷贝构造函数的调用,而本身就是在定义拷贝构造函数,如此会引起拷贝构造函数的递归调用,一旦遇到使用拷贝构造的地方会引起函数的无限递归调用而崩溃。
**拷贝构造被调用的几种情况:**对象的非指针或引用方式作为函数的返回值;对象的非指针或引用方式作为函数的参数;用一个对象去创建另一个对象。
为什么要避免拷贝构造函数的调用:会引起新对象的创建,效率低,资源消耗多;如果类中有指针类型的成员,又没有处理好深拷贝浅拷贝的问题,会引起错误。
深拷贝浅拷贝的问题:提供的默认拷贝构造函数是浅拷贝函数,调用时将对象中的数据进行逐字节拷贝,如果类中的成员变量都是非指针类型的变量,使用默认的拷贝构造函数是没问题的,但是如果有指针类型的成员变量,此时默认的拷贝构造函数,在执行到对该变量的数值处理时,是将指针的值直接赋值给要创建的对象,此时就会出现一个问题,原对象的指针和新创建的对象中的指针类型的成员变量指向同一块地址,当释放其中一个对象时,指针类型的成员变量会将指向的内存进行释放,但是此时另一个对象指针类型的成员变量仍指向那块内存,当释放两一个对象时,又会释放一次该内存,引起double free,所以在一个类中有指针类型的成员变量时需要自己重写拷贝构造函数,将指针类型的成员分配与原对象一样的内存,然后把数据拷贝到新创建的内存中,也就是所谓的深拷贝,这样前后两个对象分别指向不同的内存块,避免了double free问题的发生。
参数传递的几种方式
值传递: 使用时会将值复制一份,将复制的值传递到被调用的函数中进行使用,参数在被调用函数中的修改不会影响外部函数中的值
指针传递: 为值取一个别名传递给被调用函数使用,在函数中对参数值的修改会引起外部函数中数值的修改
应用传递: 创建一个指针,指向原来的地址,将这个指针作为参数传递给被调用函数使用,数值在被调用函数中的修改会引起外部函数中数值的修改
指针和引用的异同: 同:都可以对所指向的对象进行修改
异:指针是变量,内容是一个地址;引用只是为对象起了一个别名;
指针可以在声明的时候不进行赋初值,引用在声明的时候必须赋初值;
指针可以改变指向的对象,引用不可以;
指针可以为空,引用不可以为空;
可以有多级指针,没有多级引用;
++操作不一样;
求sizeof不一样,指针求sizeof为常量4,而对引用求sizeof是引用对象所占用的内存大小。