一、构造函数
为了更好的说明构造函数,首先建立一个简单的日期类
Date.cpp
class Date
{
public:
void DatePrint();
void DateSet(int year, int month, int day);
private:
int m_year;
int m_month;
int m_day;
};
void Date::DateSet(int year, int month, int day)
{
m_year = year;
m_month = month;
m_day = day;
}
void Date::DatePrint()
{
cout << m_year << "-" << m_month << "-" << m_day << endl;
}
1.成员变量命名风格
看到一些代码我们可能看的一脸僵硬,例如:
void Date::DateSet(int year, int month, int day)
{
year = year;
month = month;
day = day;
}
这里的 year, month, day 到底是成员变量呢,还是函数形参呢,所以在创建成员变量的时候,我们最好加上前缀或后缀用来区分,例如:
void Date::DateSet(int year, int month, int day)
{
m_year = year;
m_month = month;
m_day = day;
}
2. 概念
构造函数是一个特殊的成员函数,名字与类相同,创建类型对象时由编译器自动调用,在类的生命周期内只调用一次
3. 特性
(1) 函数名与类名相同
(2) 无返回值
(3) 对象实例化时编译器自动调用对应的构造函数
(4) 构造函数可以重载
4. 无参构造函数
如果类中没有显式定义的构造函数,C++编译器会自动的生成一个无参的构造函数,用户显式定义,编译器将不再生成,也许有人会疑问无参的构造函数看起来没什么用,编译器为什么要生构造函数呢?我们建立一个 Time 类,代码如下:
class Date
{
public:
void DatePrint();
void DateSet(int year, int month, int day);
private:
// 内置类型
int m_year;
int m_month;
int m_day;
// 自定义类型
Time m_t;
};
class Time
{
public:
Time();
private:
int m_hour;
int m_minute;
int m_second;
};
Time::Time()
{
cout << "Time()" << endl;
m_hour = 10;
m_minute = 14;
m_second = 15;
}
我们可以发现,对于上面的代码,编译器生成的默认构造函数会对自定义类型成员 m_t 调用它的默认成员函数
另外,如果通过无参构造函数创建对象时,不用跟括号,否则就成了函数声明,例如:
void DateTest()
{
Date d1;
Date d2();
}
d1 为调用无参构造函数,d2 为函数声明,该函数无参,返回值为日期类型的对象
5. 带参构造函数
Date::Date(int year , int month, int day)
{
m_year = year;
m_month = month;
m_day = day;
}
调用带参构函数
void DateTest()
{
Date d3(2018, 11, 21);
}
二、析构函数
1. 概念
程序在对象在销毁时会自动调用析构函数,完成类的一些资源清理工作
2. 特性
(1) 析构函数名是在类名前加上字符 ~
(2) 无参数无返回值
(3) 一个类有且只有一个析构函数,若未显式定义,系统会自动生成默认的析构函数
(4) 对象生命周期结束时,C++编译系统系统自动调用析构函数
(5) 析构函数不可以重载
3. 编译器生成的默认析构函数对会自定类型成员调用它的析构函数
class String
{
public:
String(const char* str = "test");
~String();
private:
char* m_str;
};
String::String(const char* str)
{
m_str = (char*)malloc(strlen(str) + 1);
strcpy(m_str, str);
}
String::~String()
{
cout << "~String()" << endl;
free(m_str);
}
class Person
{
private:
String m_name;
int m_age;
};
4. 一个类中如果涉及到了资源的管理,析构函数一定要给出
三、拷贝构造函数
1. 概念
拷贝构造函数,它是由编译器用来完成一些基于同一类的其他对象的构建及初始化。其唯一的参数(对象的引用)是不可变的(常用const类型)
2. 特性
(1) 拷贝构造函数是一种特殊的构造函数,是构造函数的一个重载形式
(2) 拷贝构造函数的参数只有一个且必须使用引用传参,如果使用传值方式传递,它将会引发无穷递归调用
(3) 若未显示定义,系统生成默认拷贝构造函数,默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝,这种拷贝只对对象中的数据成员进行简单的赋值
来一段代码检验一下:
class Date
{
public:
Date(int year = 2018, int month = 11, int day = 23);
Date(const Date& d);
private:
int m_year;
int m_month;
int m_day;
};
Date::Date(int year, int month, int day)
{
m_year = year;
m_month = month;
m_day = day;
}
Date::Date(const Date& d)
{
m_year = d.m_year;
m_month = d.m_month;
m_day = d.m_day;
}
3. 默认拷贝构造函数
代码说明:d2调用默认拷贝构造,d2和d1的值一样
#include <iostream>
using namespace std;
class Date
{
public:
Date(int year = 2018, int month = 11, int day = 23);
private:
int m_year;
int m_month;
int m_day;
};
Date::Date(int year, int month, int day)
{
m_year = year;
m_month = month;
m_day = day;
}
void Test()
{
Date d1;
Date d2(d1);
}
4. 一个类中如果涉及到了资源的管理,拷贝函数一定要显式提供,否则会共用同一个空间,该空间被多次释放,从而引起程序的崩溃,不信你看
class String
{
public:
String(const char* str = "word");
~String();
private:
char* _str;
};
String::String(const char* str)
{
_str = (char*)malloc(strlen(str) + 1);
strcpy(_str, str);
}
String::~String()
{
cout << "~String()" << endl;
free(_str);
}
void test()
{
String s1("hello");
String s2(s1);
}