构造函数
是一种特殊的方法,主要用来在创建对象时初始化对象,即为对象成员变量赋初始值,总与new运算符一起使用。一个类可以有多个构造函数,可根据其参数个数的不同或参数类型的不同区分它们。
1)、类的成员函数名和类名相同
2)、定义时可带有参数
3)、没有返回值类型声明
4)、在没有提供构造函数时,通过类名声明对象会调用默认的构造函数
5)、一般情况下我们需要手工调用构造函数,写了构造函数就必须要用。
析构函数
与构造函数相反,当对象结束其生命周期时,如对象所在的函数已经调用完毕时,系统会自动执行析构函数。一般建立一个对象需要用到new自动调用构造函数开辟一片内存空间,delete会自动调用析构函数释放内存。
1)、C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫析构函数
2)、析构函数没有任何参数和返回值类型
3)、析构函数在对象销毁时自动调用
无参构造函数
构造函数没有形参列表,即为无参
Class MyTest
{
public:
MyTest()
{
cout<<"我是无参构造函数"<<endl;
}
~MyTest()
{
cout<<"我是析构函数"<<endl;
}
private:
//......
}
带参构造函数
有形参列表,即为有参
Class MyTest
{
public:
MyTest(int a,int b)
{
m_a = a;
m_b = b;
cout<<"我是有参构造函数"<<endl;
}
~MyTest()
{
cout<<"我是析构函数"<<endl;
}
int GetA()
{
return m_a;
}
private:
int m_a,m_b;
}
Copy构造函数
如果用相同类型的另外一个对象来初始化此对象,则调用Copy构造函数,这个知识点后面会专门来讲
Class MyTest
{
public:
MyTest(const MyTest& obj)
{
cout<<"我是赋值构造函数函数"<<endl;
}
~MyTest()
{
cout<<"我是析构函数"<<endl;
}
private:
//......
}
构造函数的调用
无参调用
MyTest t;
括号法
MyTest t1(1);//带有一个参数的构造函数
MyTest t2(1,2);//带有两个参数的构造函数
......
等号法
MyTest t1 = (1,2,3,4,5);
MyTest t2 = 5;
//用法相同,调用一个参数的构造函数把5传给形参
直接调用
MyTest t1 = MyTest(1);//调用一个参数的构造函数
MyTest t2 = MyTest(1,2);//调用两个参数的构造函数
.......
为什么需要构造函数和析构函数
对于一种特别的类成员,比如复杂,内存占比大这样的成员变量我们一般不会放到栈区,而是希望出现在堆区。
在C语言中如果要声明一个数组变量并初始化它,我们不会直接定义一个数组成员,而会定义一个指针,让他指向堆区中的数组内存。虽然这个堆中的数组不是对象的成员变量,但是我们可以通过定义的指针去访问这个数组。在这个过程中我们要初始化的有两个东西,一个是指针变量,一个是堆中的数组,一般会怎样初始化,是下面这样吗?
//栈区数组
char buf[] = "Hello World!";
char *pbuf = buf;
或者复杂一点,动态分配堆内存空间(生成一个指定长度的字符串),存在于堆中,当main函数调用结束之后如果不去手动释放内存,则会出现内存泄漏,所以必须提供free方法。整个过程是不是感觉很复杂。
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#pragma warning(disable : 4996)
int main(int argc, char** argv)
{
int i, n;
scanf("%d", &n);
char *pbuf = (char *)malloc(n+1);
for (i = 0; i < n; i++)
{
pbuf[i] = rand() % 26 + 'a';
}
pbuf[n] = '\0';
printf(pbuf);
free(pbuf);
printf("hello.. \n");
system("pause");
return 0;
}
注意:上面的char *pbuf = (char *)malloc(n+1);为什么要“+1”
在字符数组中‘\0’是一个占位标识符,存在于字符数组最后,判定字符数组结束的标识,标识这串字符到结尾了。
char buf[6] = "Hello!" 在内存中buf为“Hello!\0”,‘\0’占有空间但不会被我们看到。
因此我们在动态创建字符数组时,字符长度要在分配长度基础上额外“+1”。不然会出现程序错误。
那么在C++中我们要怎么申请堆内存字符数组呢?C++提供了一种解决捷径。就是构造函数,在初始化对象时会自动调用构造函数。如果在构造函数中这样写。
#include <iostream>
using namespace std;
#pragma warning(disable : 4996)
class Test
{
public:
Test()
{
m_name = new char[100];
strcpy(m_name, "Hello World!");
}
~Test()
{
delete m_name;
}
public:
char *m_name;
};
void main()
{
Test t;
cout << t.m_name << endl;
system("pause");
}
是不是简单多了,当main函数执行完成时会自动调用类的析构函数,只要在析构函数中使用delete这个运算符释放内存就一切简单解决了。相比C是不是又方便又高效。
总结
1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数
2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数
3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数
4 )默认拷贝构造函数成员变量简单赋值
5)只要你写了构造函数,那么你必须用。
构造析构阶段性总结
1)构造函数是C++中用于初始化对象状态的特殊函数
2)构造函数在对象创建时自动被调用
3)构造函数和普通成员函数都遵循重载规则
4)拷贝构造函数是对象正确初始化的重要保证
5)必要的时候,必须手工编写拷贝构造函数