目录
1.类和对象
(1)类和对象的三个特征:封装性,继承性,多态性
(2)系统为对象分配内存空间,不会给类分配内存空间,类和对象的关系就是这个数据类型和变量之间的关系;
(3)创建对象的个数
下面的这个题目就是对于引用的考察,问的是对象的个数,这个里面只有c1和c3是对象,其他的两个c2就是一个指针,并不是一个对象,c4就是c1的一个别名,也不是一个对象,c3虽然也是一个指针,但是c3指向了new出来的内存空间,所以c3也是一个对象;
(3)全局变量,局部变量
左边的这个题目就是先输出的就是5,第二个输出的number就是局部变量10,第三个输出的::number就是输出的全局变量,也就是3;
(4)构造函数的执行次数
这个题目考察的就是构造函数的执行次数,没执行一次这个构造函数,就会输出一个1,我们要看在这个主函数里面创建了多少个对象,每次创建一个对象,就会执行一次构造函数,就会输出一个1,这个时候创建一个对象a就会执行一次析构函数,对象b是一个数组,里面有两个数据,每个数据在创建的时候,都会执行一次构造函数,所以这个数组就会创建两个对象,执行两次这个构造函数,再会输出两个1,这个指针数组不会创建对象,所以也是不会执行构造函数的;所以这个执行语句创建这个a对象的时候,会执行一次构造函数,b[2]数组会创建两个对象,执行两次构造函数,这个数组指针不会执行构造函数;
(5)静态动态析构和构造顺序
上面的这个程序,有一个局部的静态对象,一个局部的动态对象,一个全局对象,这个程序的执行顺序是这样的:
我们首先就会构造这个全局的对象,然后进入这个主函数,第一次调用这个func函数的时候,先构造这个静态对象(static修饰的对象),再去构造这个局部的动态对象,这个函数执行完毕之后,会把这个动态的对象析构掉,静态的对象不会被析构,因为我们之前学习这个static的时候就已经知道,出了作用域之后,这个static修饰的变量的数值会被保留的,我们再次进入这个作用域的时候就会恢复到之前的数值,因此这个静态的局部对象不会被析构掉,第二次调用func函数的时候,就会先创建这个动态的局部变量,因为这个static修饰的静态的局部变量没有被析构掉,因此这个静态对象不会执行构造函数,func结束之后,会把这个动态的局部对象析构掉,当程序执行完毕之后,才会把这个静态的对象析构掉,然后再去析构这个全局对象;
(6)初始化顺序和声明顺序
对象成员的构造函数的调用顺序取决于这些对象成员在这个类里面声明的顺序,与这个初始化列表里面的初始化的顺序没有关系;
向上面的这个声明顺序是bac初始化列表里面的顺序是cba,这个初始化顺序就是声明的顺序,也就是bac,与这个初始化列表的顺序没有关系;
通过下面的这个题目,我们也是可以理解这一点的,这个是c类;里面定义了ab类的对象作为成员变量,这个时候按照声明的顺序,就会先执行这个b的构造函数,再执行a的构造函数,再调用c的构造函数,析构函数的执行顺序和这个顺序恰好是相反的;
(7)构造和复制构造
这个题目用到了拷贝构造的相关知识,就是使用这个d1赋拷贝给d2相当于是使用已经初始化的对象d1去初始化对象d2;
这个时候调用d1的构造函数,使用5去初始化这个成员变量,调用构造函数就会输出1,d1赋值构造p2的时候就会输出2,创建新的对象的时候,使用这个pd指针指向他,就会调用第一个构造函数,先会输出1,然后就会调用这个geta函数,输出的就是指针指向对象初始化数值8,析构指针的时候就会输出8,然后析构d12d2就会输出两个5;
(8)拷贝构造的三种情况和例题讲解
拷贝构造函数的三种执行情况:就是下面的这个选择题的三个选项ACD,这个就是调用拷贝构造函数的三种情况;
1.用一个对象初始化同类的另外一个对象;
2.函数的形参是一个对象,而且我们调用这个函数的时候;
3.函数的返回值是类的对象,函数执行返回调用调用的时候;
首先用一个对象去初始化另外的一个对象,就是上面展示的代码,这个如何区分初始化和赋值,就是只要在定义这个类型的时候进行的赋值操作都叫做初始化,定义这个变量或者是对象之后进行操作就是赋值,上面的第一种情况里面展示的两种都是属于初始化的,而不是赋值;
这个里面就是就是调用拷贝构造函数的第二种情况,就是这个自定义的函数里面的参数是一个类,这个时候a1就是实参a2的复制品;
这个里面,自定义函数的返回值是一个类的对象,这个时候拷贝构造函数同样会被调用;
下面通过两个例题实际认识一下这个拷贝构造函数的执行:
这个题目表面上问的是这个程序的输出结果,实际上就是拷贝构造函数的执行次数,这个只有拷贝构造函数被执行的时候,才会有输出;
这个时候因为这个自定义函数的数据类型是一个引用类型,所以不会执行拷贝构造函数,函数里面的u就是一个已经存在的对象,t就是新的对象,这个时候用已经存在的对象去初始化新的对象的时候,就会调用这个拷贝构造函数,返回值是类的对象,因此也会执行拷贝构造函数,在这个题目里面就执行了两次拷贝构造函数,因此打印结果就是两个1;
如果把这个题目的自定义函数的参数改为普通的对象,而不是引用,这个时候就会执行三次拷贝构造函数,同时这个题目就会包括拷贝构造函数执行的所有情况,因此请读者下去仔细体会;
这个也是让我们去考虑调用了哪些函数,输出结果的判断首先要清楚这个函数的执行顺序以及执行情况是怎样的;
定义对象c1的时候,这个会先执行一次这个构造函数,输出1;
这个时候就会使用默认构造函数的语句进行初始化,调用show函数的时候,这个首先就是函数的参数是一个类的对象,就会执行拷贝构造函数,输出结果3;
因为前面是调用的默认的构造函数初始化,这个时候的成员变量的值就是字符A,被打印输出;
定义对象c2的时候就会调用我们自己写的有一个参数的构造函数,输出2,然后调用show函数,同样会执行这个拷贝构造函数,输出3,然后是这个打印输出成员变量的值,也就是这个构造函数执行时候传递进去的B字符;
2.继承和派生
(1)派生的构造和析构
这个程序里面的A是子类,B是公共继承A的派生类,所以这个程序在执行的时候,先会去执行a的构造函数,再去执行b的构造函数,new a的时候又会去调用a的构造函数;
这个析构的时候,先去析构掉这个派生类,delete p的时候,就会执行a类的析构,因为这个指针执行的是a类里面的内存,这个时候再去析构基类;
(2)赋值的兼容性规则
例题:派生的兼容性规则简单运用体会这个赋值兼容性怎么提现的
这个派生的兼容性规则我们可以通过下面的这个实例进行理解,基类和派生类之间到底是如何兼容的,首先我们看一下下面的这个基类和派生列之间都是公共继承的;
创建基类对象obj1,创建派生类对象obj2和obj3,然后去调用函数,第一次调用func函数传递基类对象的地址,打印的就是base class,第二次调用这个函数,虽然传递的是派生类对象的地址,但是func函数里面基类的指针只能访问基类的who,(这个地方就体现了赋值兼容性规则里面的派生类对象的地址传递给基类的指针),打印结果还是base class 下一个同理,我们使用obj2调用这个函数的时候才会输出这个derivel class,使用obj3调用这个函数的时候,才会输出derive2 class;
3.虚函数
(1)下面的这个题目既可以帮助我们了解虚函数,也可以帮助我们了解这个复制兼容性规则;
定义基类的对象b,和基类的指针p指向派生类D,使用这个D创建对象d,然后依次调用这个函数,第一个函数的参数就是基类指针,第二个函数的参数就是基类的引用,第三个函数的参数就是派生类的对象;
首先我们主函数里面调用第一个函数,传递的参数就是p指针,也就是基类的指针,这个基类的指针可以访问到派生类里面的成员函数,因为我们创建一个派生类D,指针指向他;(基类的指针指向派生类的对象的地址)
第二次调用函数,参数是对象的引用,而且是基类对象的引用,两个都是基类,输出的就是B;
第三次调用函数传递的是类,函数的参数也是类,这个时候类和类之间的赋值就要考虑这个赋值兼容性规则,实参是派生类对象,形参是基类对象,这个时候派生类对象只能访问到属于基类的部分,因此打印结果还是D;因为这个赋值兼容性规则里面就说了,这个func3函数里面只能访问到属于基类的部分,也就是这个func3函数形参b只能接受这个派生类里面属于基类的一部分,新派生出来的这个形参是不能接受的;
(2)虚函数的第二个题目
这个基类里面有一个虚函数,一个不是虚函数;
obj这个派生类的对象,函数参数是基类的引用,调用fun1函数,只会访问这个基类的成员函数,调用fun2就会访问这个派生类里面的函数,输出deprived;
(3)虚函数的第三个题目
这个是连续的两个派生,我们到底该如何理解这个虚函数,就是可以理解为这个虚函数就是实际是不存在的,执行的时候执行的是这个派生类的虚函数;
定义基类的aa对象和p指针,这个时候没有任何输出,然后定义一个派生类two的对象,bb这个时候就会执行构造函数,输出第一个2,定义three类的时候,先会去执行父类2的函数,2又回去执行自己的父类1的函数,但是1这个父类函数是虚函数,所以只会打印一个2,就是第二个2;
然后基类的指针p指向派生类对象cc,调用f函数的时候,先调用父类的f函数,输出1,再输出3;
(4)虚函数的第四个问题
我们创建了一个基类的对象,fn传参,形参是基类的引用,但是因为这个基类的虚函数和子类的虚函数的函数的参数不一样,因此这个只会调用基类里面定义的函数;
标签:这个,函数,对象,C++,---,lijiajia,调用,基类,构造函数 From: https://blog.csdn.net/binhyun/article/details/139543367