类的6个默认的成员函数:
类中如果什么都没有定义:---有六个默认的成员函数:
构造函数的概念:
构造函数的特性
为什么要引出构造函数这一概念
#include
using namespace std;
class Date
{
public:
void InitDate(int year, int month, int day) //进行初始化的操作
{
_year = year;
_month = month;
_day = day;
}
void PrintDate() //打印进行检测
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1, d2, d3;
d1.InitDate(2020, 4, 30);
d1.PrintDate();
d2.InitDate(2020, 4, 29);
d2.PrintDate();
d3.InitDate(2020, 4, 28);
d3.PrintDate();
}
构造函数的功能
#include
using namespace std;
class Date
{
public:
//无参的构造函数
Date() //构造函数 ,没有返回值
{
cout << "Date()" << this << endl; //打印this是为了看当前构造的是哪一个对象
//把对象的地址打印出来
}
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
//两个构造函数形成了重载
//一个是没有参数的构造函数,一个是具有三个参数的构造函数
//创建几个对象,编译器就会调用几次构造函数
//调用构造函数的次数与对象的次数是相同的
//因为构造函数调用的时机就是在创建对象的时候调用
void InitDate(int year, int month, int day) //进行初始化的操作
{
_year = year;
_month = month;
_day = day;
}
void PrintDate() //打印进行检测
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1, d2, d3;
Date d4(2020, 4, 27);
Date d5();
//并不是创建了一个对象
//这是一个函数声明,相当于我是有一个函数名称
//为d5的函数,这个函数的返回值类型是Date类型,没有参数
d1.InitDate(2020, 4, 30);
d1.PrintDate();
d2.InitDate(2020, 4, 29);
d2.PrintDate();
d3.InitDate(2020, 4, 28);
d3.PrintDate();
d4.PrintDate();
}
如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成
#include
using namespace std;
class Date
{
public:
//无参的构造函数
//Date() //构造函数 ,没有返回值
//{
// cout << "Date()" << this << endl; //打印this是为了看当前构造的是哪一个对象
// //把对象的地址打印出来
//}
//Date(int year, int month, int day)
//{
// _year = year;
// _month = month;
// _day = day;
//}
//两个构造函数形成了重载
//创建几个对象,编译器就会调用几次构造韩素
void InitDate(int year, int month, int day) //进行初始化的操作
{
_year = year;
_month = month;
_day = day;
}
void PrintDate() //打印进行检测
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
//编译器生成的构造函数是默认的构造函数
//默认的构造函数一定是无惨的
//如果说,用户定义构造函数了,编译器就不会生成的
//如果这个时候再创建一个带有三个参数的对象的话
//就一定是不会创建成功的
//对象创建成功了,就说明是有默认的构造函数存在的
return 0;
}
无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数
#include
using namespace std;
class Date
{
public:
//无参的构造函数
Date() //构造函数 ,没有返回值
{
cout << "Date()" << this << endl; //打印this是为了看当前构造的是哪一个对象
//把对象的地址打印出来
}
//全缺省的构造函数
Date(int year=2000, int month=1, int day=1)
{
_year = year;
_month = month;
_day = day;
}
//两个构造函数形成了重载
//创建几个对象,编译器就会调用几次构造函数
void InitDate(int year, int month, int day) //进行初始化的操作
{
_year = year;
_month = month;
_day = day;
}
void PrintDate() //打印进行检测
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
}
上面的代码,是会报错的,无法通过编译,报错的内容是—“Date::Date”:对重载函数的调用不明确,因为上面的代码中,有两个默认的构造函数,因为不带参数的构造函数和全缺省的构造函数都被看为默认的构造函数,所以说,现在有两个构造函数,编译器不知道到底要去调用哪个构造函数,所以说,就会报错,所以说,默认的构造函数只能存在有一个,下面的代码就可以通过编译了:
#include
using namespace std;
class Date
{
public:
//全缺省的构造函数
Date(int year = 2000, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//两个构造函数形成了重载
//创建几个对象,编译器就会调用几次构造函数
void InitDate(int year, int month, int day) //进行初始化的操作
{
_year = year;
_month = month;
_day = day;
}
void PrintDate() //打印进行检测
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
}
关于编译器生成的默认成员函数,很多人会有疑惑:在我们不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象year/month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用?
class Time
{
public:
Time()
{
cout << "Time()" << endl;
_hour = 0;
_minute = 0;
_second = 0;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year;
int _month;
int _day;
// 自定义类型
Time _t;
};
int main()
{
Date d;
return 0;
}
成员变量的命名风格
// 我们看看这个函数,是不是很僵硬?
class Date
{
public:
Date(int year)
{
// 这里的year到底是成员变量,还是函数形参?
year = year;
}
private:
int year;
};
// 所以我们一般都建议这样
class Date
{
public:
Date(int year)
{
_year = year;
}
private:
int _year;
};
// 或者这样。
class Date
{
public:
Date(int year)
{
m_year = year;
}
private:
int m_year;
};
// 其他方式也可以的,主要看公司要求。一般都是加个前缀或者后缀标识区分就行。
再谈构造函数
问题:构造函数体中的语句是不是初始化?
构造函数体赋值
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
初始化列表的概念
#include
using namespace std;
class Date
{
public:
Date(int year, int month, int day)
:_year(year),
_month(month),
_day(day)
{
cout << "Date(int year, int month, int day)" << endl;
}
private:
int _year;
int _month;
int _day;
};
#include
using namespace std;
class Date
{
public:
Date(int year, int month, int day)
:_year(year),
_month(month),
_day(day),
_day(day)
{
cout << "Date(int year, int month, int day)" << endl;
}
private:
int _year;
int _month;
int _day;
};
初始化列表的功能
为什么month会是随机值呢,因为编译器先去初始化year,然后初始化year完成之后,编译器开始去初始化month,但是初始化列表给的是用day去初始化month,但是此时day并没有进行初始化的操作,所以最终看出,month为随机值。
注意事项
拷贝构造函数也可以有初始化列表
#include
using namespace std;
class Date
{
public:
Date(const Date& d)
: _year(d._year)
, _month(d._month)
, _day(d._day)
{
// _year = d._year;
// _month = d._month;
// _day = d._day;
cout << "Date(Date&):" << this << endl;
}
private:
int _year;
int _month;
int _day;
};
引用变量以及const类型的变量的注意事项
#include
using namespace std;
class Date
{
public:
#if 0
// 初始化列表
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{
// 构造函数体中是赋初值不是初始化
// _year = year;
// _month = month;
// _day = day;
//r = _day;
cout << "Date(int,int,int):" << this << endl;
}
#endif
// 初始化列表
// 1. 初始化列表中成员的出现次序,不代表其真正的初始化次序
// 2. 成员变量在初始化列表中的初始化次序为其在类中的声明次序
// 建议:最好不要使用成员初始化成员
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
, _r(_day)
, _a(_day)
{
// 构造函数体中是赋初值不是初始化
// _year = year;
// _month = month;
// _day = day;
//r = _day;
cout << "Date(int,int,int):" << this << endl;
}
Date(const Date& d)
: _year(d._year)
, _month(d._month)
, _day(d._day)
, _r(d._r)
, _a(d._a)
{
// _year = d._year;
// _month = d._month;
// _day = d._day;
cout << "Date(Date&):" << this << endl;
}
// d1 = d2 = d3;
Date& operator=(const Date& d)
{
cout << this << "=" << &d << endl;
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
~Date()
{
cout << "~Date():" << this << endl;
}
int _year;
int _month;
int _day;
int& _r;
const int _a;
};
#include
using namespace std;
class Time
{
public:
Time(int hour, int minute, int second)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
, _t(0, 0, 0)
//, _t() //调用无参的默认的构造函数
{
cout << "Date(int,int,int):" << this << endl;
}
private:
int _year;
int _month;
int _day;
Time _t;
//如果没有上面的_t(0, 0, 0),直接给出Time_t是会报错的,原因在于
//声明了一个time类的对象t,那么这个对象t一定是需要进行初始化的
//那么编译器会默认去Time类中寻找没有参数的构造函数,但是Time类此时是没有
//显示给出无参的构造函数的,所以就会出错
//那么我们为了可以正确的创建出来这个Time类类型的对象的话,那么我们就需要给出来一个有参数的
//就好比说_t(0, 0, 0),就可通过编译了
};
int main()
{
//Date d(2019, 3, 24);
Date d;
return 0;
}
#include
using namespace std;
class Time
{
public:
Time(int hour = 0, int minute = 0, int second = 0)
: _hour(hour)
, _minute(minute)
, _second(second)
{
cout << this->_hour << endl;
}
void TestFunc()
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
// : _year(year)
// , _month(month)
// , _day(day)
//, _t() //调用无参的默认的构造函数
{
cout << "Date(int,int,int):" << this << endl;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
#include
using namespace std;
class Time
{
public:
Time(int hour = 0, int minute = 0, int second = 0)
: _hour(hour)
, _minute(minute)
, _second(second)
{
cout << this<< endl; //我的意图是要去打印this,然后发现代码可以正常通过编译
//那么,也就是说,此时,我的对象已经构造好了
}
private:
int _hour;
int _minute;
int _second;
};
两个问题
在构造函数有没有把对象的地址传递过去呢?
在构造函数的初始化列表的位置可不可以使用this指针呢?
#include
using namespace std;
class Time
{
public:
Time(int hour = 0, int minute = 0, int second = 0)
: _hour(hour)
, _minute(minute)
, _second(second)
{
cout << this->_hour << endl;
}
void TestFunc()
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
// : _year(year)
// , _month(month)
// , _day(day)
//, _t() //调用无参的默认的构造函数
{
cout << "Date(int,int,int):" << this << endl;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
int main()
{
// 在编译器编译期间,已经为main分配好了栈空间
// 该空间中已经包含了函数体中的局部对象
Date d; // 在构造函数调用之前,对象是不存在的
Time t;
t.TestFunc();
return 0;
}
结论:也就是说对象的空间早就已经给好了,只不过缺的是构造函数,构造函数的功能就是把对象中各个成员变量给其初始化好就可以了
构造函数的功能:
explicit关键字
#include
using namespace std;
class Date
{
public:
Date(int year)
: _year(year)
{
cout << "Date(int,int,int):" << this << endl;
}
Date& operator=(const Date& d)
{
return *this;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d(2019);
d = 2020;
//一个是日期类型的变量,一个是整形的变量,在我们之前想的是,这时不能通过编译的
//但是没想到,这样的代码是可以通过编译的,原因在于
// 2020---> 通过单参构造函数--->临时对象
//也就是说构造函数具有类型转换的功能,本来是一个int类型,然后被转换了
// 用一个整形变量给日期类型对象赋值
// 实际编译器背后会用2020构造一个无名对象,最后用无名对象给d1对象进行赋值
//但是一般情况下,会把这种类型转换禁止掉,那么如何来禁止呢?
//禁止的方法就是在构造函数前面加上一个explicit关键字
return 0;
}
#include
using namespace std;
class Date
{
public:
explicit Date(int year)
: _year(year)
{
cout << "Date(int,int,int):" << this << endl;
}
Date& operator=(const Date& d)
{
return *this;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d(2019);
d = 2020; // 2020---> 通过单参构造函数--->临时对象
return 0;
}
之前说过的几个默认的构造函数,如果我么没有显示给出的话,编译器会生成默认的
#include
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d;
return 0;
}
#include
using namespace std;
class Time
{
public:
Time(int hour = 0, int minute = 0, int second = 0)
: _hour(hour)
, _minute(minute)
, _second(second)
{
cout << this->_hour << endl;
}
Time(Time&)
{}
Time& operator=(Time& t)
{
return *this;
}
~Time()
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
// Date()
// {}
/*
Date(Date& d)
: _t(d._t)
{}
*/
private:
int _year;
int _month;
int _day;
Time _t;
};
int main()
{
Date d1; // 日期类的构造函数 // Time()
Date d2(d1); // Time(Time&)--->找个调用位置--->Date类的拷贝构造函数中
Date d3;
d3 = d1;
return 0;
}
谢谢大家的支持!