万事开头难,形成习惯是最重要的。
学习基于B站黑马的视频,从类和对象部分开始,笔记有错误的地方欢迎指正。
1.构造函数和析构函数
构造函数(constructor)和析构函数(destructor)分别负责对象(class)的初始化和清理工作,定义类时,不在类的内部提供的话编译器(compiler)会自动提供空实现。
构造函数名和类名相同,无返回值且不用写void,可重载,创建class时自动调用且只调用一次。
析构函数名和类名相同,函数名前加符号~,无返回值且不用写void,无参数(不可重载),class销毁前调用且只调用一次。
构造函数分类:1.按参数分 -- 有参&无参(默认) 2.按类型分 -- 普通&拷贝
三种转换方式:1.括号法 2.显示法 3.隐式转换法
#include <iostream>
using namespace std;
class person{
public:
//构造函数
person(){
cout << "构造函数的调用" << endl;
}
person(int a){
age = a;
cout << "有参构造函数的调用" << endl;
}
person(const person &p){
age = p.age
cout << "拷贝构造函数的调用" << endl;
}
//析构函数
~person(){
cout << "析构函数的调用" << endl;
}
int age;
};
void test(){
//1.括号法
person p1; //默认构造,栈上的数据,test执行完毕后会将对象释放,会出现析构调用字样
person p2(10); //有参构造
person p3(p2); //拷贝构造
//注意:调用默认构造时不要加(),会被编译器认为是在函数声明而不是创建对象
//如:person p1();
//2.显示法
person p1; //默认
person p2 = person (10); //有参
person p3 = person (p2); //拷贝
//3. 隐式转换法
person p1; //默认
person p2 = 10; //有参
person p3 = p2; //拷贝
cout << "p2的年龄为:" << p2.age << endl;
}
int main(){
//test();
person p; //对象创建后系统中断,不会出现析构调用字样
system("pause");
return 0;
}
构造函数的调用规则
1.用户定义有参构造,C++不再提供默认无参构造,但提供默认拷贝构造。
2.用户定义拷贝构造,C++不再提供其他构造。
2.深拷贝和浅拷贝
浅拷贝(shallow copy):简单的赋值拷贝操作
深拷贝(deep copy):在堆区(heap)重新申请空间进行拷贝操作
#include <iostream>
using namespace std;
class person {
public:
//构造函数
person() {
cout << "默认构造函数的调用" << endl;
}
person(int age, int height) {
m_age = age;
m_height = new int(height);
cout << "有参构造函数的调用" << endl;
}
//析构函数
~person() {
//将堆区开辟数据释放
if (m_height != NULL) {
delete m_height;
m_height = NULL;
}
cout << "析构函数的调用" << endl;
}
int m_age; //年龄
int *m_height; //身高
};
void test01() {
person p1(18, 160);
cout << "p1年龄为:" << p1.m_age << "身高为:" << *p1.m_height << endl;
person p2(p1);
cout << "p2的年龄为:" << p2.m_age << "身高为:" << *p2.m_height << endl;
}
int main() {
test01();
system("pause");
return 0;
}
上面的代码为利用编译器提供的拷贝构造函数进行拷贝操作,此时为浅拷贝,会导致堆区内存的重复释放,导致系统报错:
利用深拷贝解决问题:
//在类中自定义拷贝构造如下
person(const person &p) {
cout << "拷贝构造函数的调用" << endl;
m_age = p.m_age;
m_height = new int(*p.m_height); // 深拷贝操作,开一个新空间
// 编译器的默认实现为:m_height = p.m_height;
}
如果有属性在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝问题。
3.类对象作为类成员
当其他类对象作为本类成员,构造时先构造该类对象,再构造自身,析构的顺序反之。