1.如果一个类什么成员都没有,简称空类
但是空类真的什么都没有吗?
并不是,任何类什么都不写,编译器会自动生成以下6个默认成员函数
1.构造函数
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void print(void)
{
cout << _year << _month << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main(void)
{
Date d1;
d1.Init(2005, 4, 14);
d1.print();
return 0;
}
对于Date类,可以通过Init共有的方法给对象设置信息,但是每次创建新的对象,都要取调用Init函数,好麻烦啊!那能不能在创建对象的时候就把信息设置进去呢?
这个时候就可以用构造函数了
class Date
{
public:
Date (int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date()
{
}
void print(void)
{
cout << _year << _month << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main(void)
{
Date d1(2005, 4, 14);//调用带参数的构造函数
Date d2;//调用无参构造函数
Date d3();//编译器会报错
//如果通过无参构造函数创建对象的时,对象后面不用跟括号,否则就成了函数声明,比如d3
d1.print();
return 0;
}
这个地方Date d1(2005,4,14)不会被当作函数声明是因为,函数声明里面是类型
但是Date d3()编译器就无法判断了!
构造函数是特殊的成员函数,但需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象!
其特征如下
1.函数名与类名相同(都是Date)
2.无返回值(不能写返回值,包括void也不可以)
3.对象实例化时编译器自动调用对应的构造函数
4.构造函数可以重载
那么如果我没有显示定义构造函数,则C++编译器会自动生成一个无参的默认的构造函数,一旦用户显示定义编译器将不再生成
这也就是为啥上面两个例子,不管我写不写构造函数
Date d1;
Date d2;
这种都可以编译通过
本质上编译器帮你写了一个
Date()
{
}
因此你会发现如果我这
因此你发现如果我这样写编译器就会报错
本质上是因为我自己写了显示定义函数,导致原本的无参的默认的构造函数消失了!
但是你会发现一件很神奇的事就是,哪怕有这个默认的无参构造函数,但是我依然打印出来的是一个随机值,那么这个默认的无参构造函数的意义在哪呢?
1.一般情况下,有内置成员变量,就需要自己写构造函数,不能用编译器具体生成的
2.全部都是自定义类型成员,可以考虑让编译器自己生成
此外,这个地方是随机值的原因是没有初始化
(1).内置类型,基本类型,语言本身定义的基础类型,
如int /char/double/指针等
(2).自定义,struct /class等等定义的类型
我们对于内置类型等等(1)编译器不做处理,自定义类型(2)会去调他的默认构造
当然有些编译器也会对内置类型等等(1)处理,但那是个性化行为,并不是所有编译器都这么做!
注意!:C++11中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明可以给默认值
就像下面这样
class Date
{
public:
void print(void)
{
cout << _year << _month << _day << endl;
}
private:
//内置类型 C++11支持 这里不是初始化只是声明 不开辟空间
//这里给的是默认的缺省值,给编译器生成默认构造函数用
int _year=2005;
int _month=4;
int _day=14;
};
int main(void)
{
Date d1;
d1.print();
return 0;
}
但是如果自己写了显示定义函数,那个缺省值就没用了!
有了缺省值,我们传参传几个都可以!同样这种由于构造函数支持重载,也需要和重载函数一样避免歧义!
无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数和全缺省构造函数,我们没写编译器默认生成的构造函数,都可以认为是默认的构造函数
2.析构函数
构造函数可以帮助我们完成初始化,那么有没有什么函数可以帮助我们完成销毁呢?
答案就是析构函数
class stack
{
public:
stack(size_t capacity = 3)//构造函数
{
_arry = (Datetype*)malloc(sizeof(Datetype) * capacity);
if (_arry == NULL)
{
perror("malloc fail");
}
_capacity = capacity;
_size = 0;
}
void push(Datetype data)
{
//Checkcapacity()这个地方还有一个检查容量的函数暂时省略不写
_arry[_size] = data;
_size++;
}
~stack()//析构函数
{
if (_arry)
{
free(_arry);
_arry = 0;
_capacity = 0;
_size = 0;
}
}
private:
Datetype* _arry;
int _capacity;
int _size;
};
int main(void)
{
stack d1(66);
d1.push(1);
d1.push(11);
return 0;
}
析构函数是特殊的成员函数,其特征如下:
1.析构函数名是在类名前面加一个~
2.无参数无返回值(无返回值也不能写void)
3.一个函数只能有一个析构函数,若未显示定义,系统会自动生成默认的析构函数。
注意:析构函数不能重载
4.对象生命周期结束时,C++编译系统会自动调用析构函数
比如上面那个例子就是在return 0;前调用析构函数!
我们来看下面这个例子
class Time
{
public:
~Time()
{
cout << "~Time()" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class date
{
private:
int _year=2005;
int _month=4;
int _day=14;
Time arry;
};
int main(void)
{
date a;
return 0;
}
为啥我调用的是date打印的却是Time的析构函数呢?
首先date里面的三个变量_year,_month,_day都是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;
但是arry是Time类是自定义类型,date销毁的时候,会将date里面的四个变量都销毁,所以销毁arry要调用Time的析构函数,
但是main函数里面不能直接调用Time的析构函数,实际要释放的是Date类的对象,所以编译器会调用date类的析构函数,
但是date没有提供显示的析构函数,因此编译器会给date生成一个默认的析构函数,目的是在其内部调用time类的析构函数,
即当date类被销毁时,要保证其内部每个自定义对像都可以正确销毁
注意:销毁哪个那个类的对象则调用那个类的析构函数