1.什么是构造函数? 创建一个对象之后,就需要初始化该对象,构造函数就是用来初始化该对象的。 2.构造函数有什么作用? 构造函数的作用是初始化对象的数据成员。类对象被创建的时候,编译系统为对象分配内存空间,并自动调用该构造函数,由构造函数完成初始化工作。 3.构造函数的分类有哪些?
- 无参构造函数
- 待默认值的构造函数
- 有参构造函数
- 拷贝构造函数
- 一种特殊的构造函数,当对象之间复制时会自动调用拷贝构造函数
- 若类中没有定义拷贝构造函数,系统会自动生成一个拷贝构造函数
- 拷贝构造函数具有单个形参,该形参(常用const修饰)是对该类型的引用,当定义一个新对象并对他进行初始化时,将显示调用拷贝构造函数,当该类型的对象传递给函数返回该类型的对象时,将隐式调用拷贝构造函数
- 当类中有一个数据成员是指针时,或者有成员表示在构造函数中分配的其它资源,必须显示调用拷贝构造函数。
4.浅拷贝和深拷贝的区别? 对于下边的拷贝构造函数,如果在拷贝name时,直接写为:
_name=src._name
那么在析构时,新对象和旧对象就会析构同一块内存空间,程序就会奔溃; 而深拷贝就是重新为新对象开辟一段内存空间,不只是简单地赋值。(就好比你的作业写完了,你的同桌拿去看了,但是你的同桌不能交你的作业,因为你还要交,而一份作业不能交两次,所以你的同桌要交作业的话,就必须照着你的作业抄一份,然后你们各交各的)
下边我们通过一个代码来详细了解构造函数:
#include<iostream>
using namespace std;
class Person
{
public:
int _age;
int _sex;
char* _name;
Person()
{
_name = NULL;
cout << "Person()" << endl;
}
Person(int age)
{
cout << "Person(int age)" << endl;
_age = age;
_name = NULL;
}
Person(int age, int sex,const char* name)
{
cout << "Person(int age, int sex, char* name)" << endl;
_age = age;
_sex = sex;
_name = new char[strlen(name) + 1];
memset(_name, 0, strlen(name) + 1);
for (int i = 0; i < strlen(name); i++)
{
_name[i] = name[i];
}
}
Person(const Person& src)
{
cout << "Person( Person src)" << endl;
_age = src._age;
_sex = src._sex;
if (NULL != src._name)
{
_name = new char[strlen(src._name) + 1];
memset(_name, 0, strlen(src._name) + 1);
for (int i = 0; i < strlen(src._name); i++)
{
_name[i] = src._name[i];
}
}
else
{
_name = NULL;
}
}
Person& operator=(const Person& src)
{
cout << "void operator=(const Person& src)" << endl;
if (this == &src)
{
return *this;
}
delete[]_name;
if (NULL != src._name)
{
_name = new char[strlen(src._name) + 1];
memset(_name, 0, strlen(src._name) + 1);
for (int i = 0; i < strlen(src._name); i++)
{
_name[i] = src._name[i];
}
}
else
{
_name = NULL;
}
}
~Person()
{
if (NULL != _name)
{
cout << _name << endl;
}
cout << "~Person()" << endl;
if (NULL != _name)
{
delete[]_name;
}
_name = NULL;
}
void work()
{
cout <<_name<<":work" << endl;
eat();
}
void eat()
{
cout << _name<<"eat" << endl;
}
void show()
{
if (NULL != _name)
{
cout << "name:" << _name << endl;
}
cout << "age:" << _age << endl;
cout << "sex" << _sex << endl;
}
};
Person fun(Person& p)
{
int a = 10;
Person tmp(10);
return tmp;
}
int main()
{
Person p1;
char name[] = { "zhangsan" };
p1._name = name;
p1.eat();
char name[] = { "zhangsan" };
Person p1(32, 1, name);
p1.show();
Person p2;
Person p3();
char name[] = { "zhangsan" };
Person p1(32, 1, name);
Person p2(p1);
p1.show();
p2.show();
Person p3(15,1,"lisi");
p3 = p2 = p1;
p3.show();
Person p1(31, 1, "zhangsan");
Person* p2 = new Person();
delete p2;
Person p3;
Person p4 = 20;
Person p5 = p3;
Person p6(p3);
Person p3;
p3 = fun(p3);
Person p4 = fun(p3);
return 0;
}
测试模块一:调用无参构造函数 测试模块二:调用有参构造函数,先构造后析构 测试模块三:先调用普通构造对成员变量进行初始化,再调用拷贝构造函数构造一个新对象p2,同时对p2的成员变量进行初始化。 测试模块四:赋值运算符的“=”是属于后边对象的,因为p2的name为空,所以p3的name也被置空 如果为“p2=p3",则执行结果就会出现name 测试模块五: 测试模块六:拷贝构造函数是用一个已经存在的对象去构造一个新的对象,而等号赋值运算符的两个对象都是已经存在的 特别注意:这里有一个优化构造概念 测试模块七:
- 先构造p3;
- 再构造临时临时对象tmp;
- 用tmp拷贝构造一个返回值对象;(return tmp)
- 函数执行完成,析构临时变量tmp;
- 因为p3已经存在,所以“p3 = fun(p3)"调用赋值运算符重载函数,用返回值对象给p3赋值;
- 析构返回值对象;
- 再构造临时对象tmp;
- 再构造返回值对象;
- 再用返回值对象构造p4;(以上三步符合优化构造的条件,所以这里直接为构造p4) ;
- 析构临时对象;
- 析构返回值对象;(相当于析构p4) 析构p3
构造函数部分参考:https://blog.csdn.net/qq_29339467/article/details/90719951.
|