知识点【构造函数的调用规则】(重要)
系统会对任何一个类提供3个函数成员函数:
默认构造函数(空) 默认析构函数(空) 默认拷贝构造函数(浅拷贝)
1、如果用户提供了有参构造 将屏蔽 系统的默认构造函数。
Data ob1;//err
2、如果用户提供了有参构造 不会屏蔽 系统的默认拷贝构造函数。
Data ob1(10);
Data ob2 = ob1;
ob2.num == 10
3、如果用户提供了拷贝构造函数 将屏蔽 系统的默认构造函数、默认拷贝构造函数。
Data ob1;//err
总结:
对于构造函数:用户一般要实现:无参构造、有参构造、拷贝构造、析构。
知识点2【深拷贝与浅拷贝】
浅拷贝:
class Person
{
private:
char *m_name;
int m_num;
public:
Person()
{
m_name = NULL;
m_num = 0;
cout<<"无参构造"<<endl;
}
Person(char *name,int num)
{
//为m_name申请空间
m_name = (char *)calloc(1,strlen(name)+1);
if(m_name == NULL)
{
cout<<"构造失败"<<endl;
}
cout<<" 已经申请好空间"<<endl;
strcpy(m_name,name);
m_num = num;
cout<<"有参构造"<<endl;
}
Person(const Person &ob)//ob==>lucy
{
cout<<"拷贝构造函数"<<endl;
m_name = (char *)calloc(1, strlen(ob.m_name)+1);
cout<<"空间已被申请"<<endl;
strcpy(m_name, ob.m_name);
m_num = ob.m_num;
}
~Person()
{
if(m_name != NULL)
{
cout<<"空间已被释放"<<endl;
free(m_name);
m_name = NULL;
}
cout<<"析构函数"<<endl;
}
void showPerson(void)
{
cout<<"m_name = "<<m_name<<", m_num = "<<m_num<<endl;
}
};
void test01()
{
Person lucy("lucy",100);
lucy.showPerson();
//浅拷贝的问题(多次释放同一块堆区空间)
//通过自定义 拷贝构造函数 完成深拷贝动作
Person bob = lucy;//调用系统的默认拷贝构造(单纯的值拷贝)
}
如果类中的成员 指向了堆区空间 一定要记得在析构函数中 释放该空间
如果用户 不实现 拷贝构造 系统就会提供默认拷贝构造
而默认拷贝构造 只是单纯的赋值 容易造成浅拷贝问题
用户记得 要实现:无参构造(初始化数据)、有参构造(赋参数)、拷贝构造(深拷贝) 、析构函数(释放空间)
知识点3【初始化列表】
注意:初始化成员列表(参数列表)只能在构造函数使用
class Data
{
private:
int m_a;
int m_b;
int m_c;
public:
//成员名(形参名)
Data(int a,int b,int c):m_a(a),m_b(b),m_c(c)
{
//m_a = a;
// m_b = b;
//m_c = c;
cout<<"有参构造"<<endl;
}
~Data()
{
cout<<"析构函数"<<endl;
}
void showData(void)
{
cout<<m_a<<" "<<m_b<<" "<<m_c<<endl;
}
};
void test01()
{
Data ob(10,20,30);
ob.showData();
}
运行结果:
知识点4【类的对象作为另一个类的成员】
class A
{
private:
int m_a;
public:
A()
{
cout<<"A无参构造函数"<<endl;
}
A(int a)
{
m_a = a;
cout<<"A有参构造函数"<<endl;
}
~A()
{
cout<<"A析构函数"<<endl;
}
};
class B
{
private:
int m_b;
public:
B()
{
cout<<"B无参构造函数"<<endl;
}
B(int b)
{
m_b = b;
cout<<"B有参构造函数"<<endl;
}
~B()
{
cout<<"B析构函数"<<endl;
}
};
class Data
{
private:
A oba;//对象成员
B obb;//对象成员
int data;//基本类型成员
public:
Data()
{
cout<<"Data无参构造"<<endl;
}
//初始化列表:对象名+() 显示调用 调用对象成员的构造函数
Data(int a, int b, int c):oba(a),obb(b),data(c)
{
//data =c;
cout<<"有参构造"<<endl;
}
~Data()
{
cout<<"Data析构函数"<<endl;
}
};
void test01()
{
//先调用 对象成员的构造-->自己的构造函数-->析构自己--->析构对象成员
//Data ob1;
//系统默认调用的是 对象成员的无参构造
//必须在Data的构造函数中 使用初始化列表 使其对象成员 调用有参构造
Data ob2(10,20,30);
}
运行结果;
注意:
1、按各对象成员在类定义中的顺序(和参数列表的顺序无关)依次调用它们的构造函数
2、先调用对象成员的构造函数,再调用本身的构造函数。 析构函数和构造函数调用顺序相反,先构造,后析构
知识点5【explicit关键字】
C++提供了关键字explicit,禁止通过构造函数进行的隐式转换。声明为explicit的构造函数不能在隐式转换中使用
class Data
{
private:
int num;
public:
//explicit 该有参构造函数 不允许 隐式转换
explicit Data(int n):num(n)
{
cout<<"有参构造"<<endl;
}
~Data()
{
cout<<"析构函数"<<endl;
}
void showNum(void)
{
cout<<"num = "<<num<<endl;
}
};
int main(int argc, char *argv[])
{
//隐私转换
//Data data = 10;//explicit 该有参构造函数 不允许 隐式转换
Data data(10);//ok
data.showNum();
Data ob=Data(10);//ok
ob.showNum();
return 0;
}
运行结果