C++编译器提供的初始化方案:
构造函数的调用方法是自动调用,也就是隐式调用,按照规则进行调用,也就是按照类名.类名()这种方式调用。
可以通过构造函数显示的初始化类的属性
类没有写构造函数,C++编译器会自动生成一个默认的构造函数。
如果都设置成为显示初始化,那么当你定义一个对象数组的时候,是没有机会进行显示初始化的。
为什么需要隐式初始化,或者是显示初始化方法的缺点?可以自动的对对象进行初始化,不用显示调用。
copy构造函数的调用。
赋值构造函数主要完成两个对象的赋值,用一个对象去初始化另外一个对象。
C++编译器会自动生成一个copy构造函数,应该是浅拷贝。
情况1:当用一个对象去初始化另外一个对象的时候,如果类中有实现的拷贝构造函数,就会调用拷贝构造函数,如果没有,就调用C++编译器自动生成的拷贝构造函数。
情况2:一个要初始化对象,调用copy构造函数,参数为另一个类对象。
赋值等号操作不会调用拷贝构造函数,不过可以进行操作符的重载,会调用C++编译器默认提供的等号操作。
情况3:类对象作为实参的函数进行调用的时候,实参会初始化形参,这个时候会调用类的拷贝构造函数。
情况4:函数的返回值是一个元素(对象,复杂类型),C++编译器会返回一个新的匿名对象,因此会调用拷贝构造函数以及析构函数,那为什么不将函数中创建的对象返回,因为这个对象是局部变量,函数调用之后就被销毁了,也就是析构了。
既然匿名对象已经返回了,那么来接受这个匿名对象?如果不接受,那么就直接析构掉了。
如果用一个刚刚创建的对象来接受(类名 对象名 = 函数的返回值)。这种情况,C++编译器会直接将匿名对象转换成为这个对象,相当于转正,从匿名转成有名字了。
这样的目的是为了加快C++的速度。相当于是牺牲一个内存,这个对象就不会立马被析构了,只有等到这个新对象的生命周期的结束,才会进行析构。
如果是创建一个对象,并且进行初始化,然后在用这个匿名对象赋值给这个初始化的对象,这个时候,这个匿名对象会被析构掉。(这个因为这个对象已经占用了内存,而上面那个例子,并没有占用内存,仅仅是将匿名对象的那个内存块给了新创建的对象。)
调用无餐构造函数不用加括号。
构造函数的调用规则和调用顺序:
两个特殊的构造函数:
当类中没有定义构造函数的时候,编译器默认提供一个无餐构造函数,并且函数体为空。
默认拷贝函数,当类中没有定义拷贝函数的时候,编译器提供一个默认的拷贝构造函数,简单进行成员变量的值复制,也就是浅拷贝。
构造函数调用研究:
1:当类中没有定义任何一个构造函数的时候,C++编译器会提供默认的无参构造函数和默认拷贝构造函数
2:当类中定义了拷贝构造函数,C++编译器不会提供无参数构造函数。
3:当类中定义了任何非拷贝构造函数,C++编译器不提供无参构造函数。
4:默认拷贝构造函数仅仅提供简单的赋值工作,也就是浅拷贝。
也就是说,只要你写了构造函数,就一定要自己调用。
构造函数总结:
构造函数是C++中用于初始化对象状态的特殊函数
构造函数在对象创建的时候自动被调用,这是一种回调的思想
构造函数和普通函数都遵循重载的规则
拷贝构造函数是对象正确初始化的重要保证
必要的时候,必须手工编写拷贝构造函数。
深浅拷贝和struct中的问题是一样的,都是由于不同的指针变量同时指向了同一片内存空间,其中一个将内存空间已经释放,导致其他指针变量在释放内存 空间的时候出现了问题。
C++中对象的等号操作其实是操作符的重载,本质还是一个浅拷贝的操作。C++编译器提供的等号操作,也是一个浅拷贝操作。不过可以使用操作符的重载来解决问题。
构造函数的初始化列表:
在一个类里B面组合了另外一个类A,A类的构造函数带有参数,但是在定义B类的时候,需要确定应该给这个对象分配多少的内存空间,由于A类的构造函数带有参数,因此只能显示的调用,而在B类中并没有显示调用初始化,因此初始化失败。
为此,C++提供初始化列表对类的成员变量进行初始化
语法:
B构造函数():A元素1初始化,A元素2初始化。。(调用对应的构造函数即可)
构造函数的调用顺序呢?
直行被组合对象的构造函数,如果组合对象有多个,按照定义的顺序,而不是按照初始化列表的顺序,析构函数和构造函数的调用顺序相反。(析构函数不能重载,只有构造函数和普通函数可以重载)
如果类的属性中有一个const这么一个属性,那么需要构造函数的时候进行初始化。
匿名对象的声明周期,如果没有对应的类对象来接替,会在被构造之后,立马析构掉。
在构造中调用构造函数,是一件危险的行为,可能生成一个匿名对象。
new和delete是操作符,不是函数,是C++的语法,和C语言中的malloc和free很类似。
new操作符可以,分配基础类型变量,分配数组类型,分配类对象
用new自动调用类写的构造函数,delete会直行类的析构函数。
用malloc分配的内存可以用delete释放么?
用new分配的内存可以用free释放么?
应该可以
new不光会分配内存,还会调用构造函数,同时可以初始化对象,
delete对调用类的析构函数,
static关键字修饰成员变量,成员函数,
静态成员提供了一种同类对象的共享机制。
成员函数可以用static修饰,就为静态成员函数。
在静态方法中可以调用普通成员属性后者是成员方法么?不可以,因为这个类有多个对象,每个对象中的变量都可能是不相同的,因此不确定是那个对象变量的值。
静态方法中只能使用静态成员变量。
C++面向对象模型的初探。
C++编译器如何管理类,对象,类和对象之间的关系的?
C++编译器是如何管理普通成员函数和静态成员函数的?
成员变量和成员函数的解析是分开存储的。
普通成员变量存储于结构体里面,和struct有这相同的内存布局和字节对齐方式。
静态成员变量存于全局数据区中
成员函数,存储于代码段中。
那么问题来了,有这么多对象,共用一块代码。代码是如何区分具体对象的呢?
普通成员函数中会隐藏一个this指针,静态成员函数没有this指针,
总结:
C++类中的成员变量和成员函数是分开存储的。C语言的内存四区模型仍然有效。
C++中类的普通成员函数都隐含了包含了一个指向当前对象的this指针
静态成员函数,成员变量属于类
静态成员函数不包含想普通成员函数一样指向自己的this指针
类的成员函数用const来修饰,修饰的是谁?
const写在函数的什么位置,是没有关系的,那为什么会这样的?
const修饰的是方法中的隐藏的this指针。也就是指针指向的内存空间是不能被修改的。修饰方法的const本质是修饰this指针,由于this指针被隐藏起来了,因此可以放在任何位置
通过添加这个const关键字,使得这个this指针变成了只读属性。
如何返回一个引用呢?return *this这样就可以了。返回引用,就是返回自身。
友元函数,通过设置友元函数可以在类外部对类的成员属性进行修改,这样就改变了类的封装的特性。
友元类,常用的一种应用和java中的反射机制是类似的,目的是为了在某些时刻,直接对另外一个类中的属性变量进行修改。
操作符的重载机制,本质是一个函数,可以用两种不同的方式来进行实现,第一种是全局函数法,为了可以直接对类的成员变量进行修改,因此,这里需要的是一个全局的友元函数。第二中方法是将这个全局函数转变成为一个类的成员方法,(这两种相互实现的方式是可以进行相关之间的转换的,区别在于使用类的成员方法中会包含一个this指针)。
全局函数和成员函数的时候会少一个参数,这个参数就是成员函数的this指针。