正文
为成员变量赋初值,分配资源,设置对象的初始状态 可以理解为类的初始化函数
#include<iostream>
using namespace std;
class STU
{
char* m_name;
int m_age;
public:
//STU(){} 缺省构造函数 无参 一旦你实现了任意一个构造函数将不再提供缺省构造
STU()
{
m_name = new char[20]{ "学生姓名" };//分配内存并初始化
m_age = 18;
}
void introduce()
{
cout << "name:" << m_name << endl << "age:" << m_age << endl;
}
};
int main()
{
STU stu;//自动调用构造函数
stu.introduce();
return 0;
}
构造函数一般用于为对象分配内存空间
和普通成员函数一样,构造函数是允许重载的。一个类可以有多个重载的构造函数,创建对象时根据传递的实参来判断调用哪一个构造函数
#include<iostream>
using namespace std;
class STU
{
char* m_name;
int m_age;
public:
STU()
{
m_name = new char[20]{ "学生姓名" };//分配内存并初始化
m_age = 18;
}
//构造函数的重载
STU(const char* name, int age)
{
m_name = new char[20];//分配了内存就要释放
strcpy(m_name, name);
m_age = age;
}
void introduce()
{
cout << "name:" << m_name << endl << "age:" << m_age << endl;
}
};
int main()
{
STU stu; //自动调用构造函数
stu.introduce();
STU stu1("程序", 20); //自动匹配并调用构造函数的重载
stu1.introduce();
return 0;
}
//打印结果
name:学生姓名
age:18
name:程序
age:20
创建对象时系统会自动调用构造函数进行初始化工作,对应的,销毁对象时系统也会自动调用一个函数来进行清理工作
//在构造代码下面追加析构函数
~STU()
{
delete[] m_name; //构造申请内存,析构释放内存
}
销毁对象时系统自动调用析构函数
为什么释放多个内存要加[]
为了测试这一情况,定义一个类
class test
{
public:
test()
{
cout << "构造" << endl;
}
~test()
{
cout << "析构" << endl;
}
};
在主函数中申请多个对象后加[]释放
int main()
{
test *pTest = new test[4];
delete[] pTest;
return 0;
}
输出结果:
构造
构造
构造
构造
析构
析构
析构
析构
不加[]释放
int main()
{
test *pTest = new test[4];
delete pTest;
return 0;
}
输出结果:
构造
构造
构造
构造
析构
可以看出不加[]只释放一个对象的内存,并且还会报错
其实呢,在申请多个内存是返回的内存地址并不是申请的内存首地址而是向右移4个字节之后的地址,我们可以打印出隐藏的4个字节内容
int main()
{
test *pTest = new test[4];
cout << *((int*)(pTest - 4)) << endl;
delete[] pTest;
return 0;
}
打印结果:
构造
构造
构造
构造
4
析构
析构
析构
析构
这样你会发现隐藏的4个字节存储了你申请的对象数量,当delete加[]时,会先访问这4个字节的数据,然后再释放内存
在构造析构顺序之前先看一下
这样我们先创建三个类(A,B,C),其中B类中有A对象,C类中有B对象
#include<iostream>
using namespace std;
class A
{
public:
A(){
cout << "A构造" << endl;
}
~A() {
cout << "A析构" << endl;
}
};
class B
{
public:
B() {
cout << "B构造" << endl;
}
~B() {
cout << "B析构" << endl;
}
A a;
};
class C
{
public:
C() {
cout << "C构造" << endl;
}
~C() {
cout << "C析构" << endl;
}
B b;
};
int main()
{
C* c=new C; //创建对象
delete c; //销毁对象
return 0;
}
//打印结果
A构造
B构造
C构造
C析构
B析构
A析构
简化成员变量初始化,仅仅只是为了书写方便,没有效率上的提升
class STU
{
string m_name; //姓名
int m_age; //年龄
int m_height; //身高
public:
STU():m_name("学生姓名"),m_age(18),m_height(180) { }
STU(string name, int age,int height)
:m_name(name)
,m_age(age)
,m_height(height) { }
void introduce()
{
cout << "Name:" << m_name << "\tAge:" << m_age << "\tHeight:" << m_height << endl;
}
};
int main()
{
STU stu;
stu.introduce();
STU stu1("程序", 20,180);
stu1.introduce();
return 0;
}
在初始化列表中需要注意的是参数初始化顺序与初始化表列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关,这个会导致什么问题呢
//将构造函数的重载修改成
STU(string name, int age,int height)
:m_name(name)
, m_height(height)
,m_age(m_height){ }
输出结果:
Name:学生姓名 Age:18 Height:180
Name:程序 Age:-858993460 Height:180
这里只是该变了初始化列表的顺序,然后用m_height成员变量去初始化m_age
造成这样后果的原因是初始化列表先初始化m_age,再初始化m_height