class Date
{
public:
private:
int _year;
int _month;
int _day;
};
上面是一个日期类;
#include<iostream>
using namespace std;
class Date
{
public:
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;//实例化
return 0;
}
一个类会有六个默认成员函数,什么叫默认成员函数?
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
上述写的是一个带参的构造函数,构造函数是可以重载的,我们也可以写成无参形式,也就是我们实例化对象时不需要传参让它具体是几年几日,我们默认都传1过去,即让它是1年1月1日。
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
我们看看效果
?
我们也可以给有参构造函数传缺省值,即
Date(int year=1, int month=1, int day=1)
{
_year = year;
_month = month;
_day = day;
}
这样我们构建实例化对象时既可以传参也可以不传参。
我们开始说了构造函数是类的默认成员函数,对于内置类型我们不写是不会初始化的。对于自定义类型不写编译器是自动会生成一个默认的构造函数。
?
对于栈satck来说也是不会。
?但是后来C++11有了改变,我们在声明成员函数的时候给出缺省值就可以对自定义类型不写构造函数也可以初始化。
如果我们这个时候 把Stack的构造函数写出来
Stack(int capacity=4)
{
_a = new int[capacity];
_top = 0;
_capacity = capacity;
}
对于用两个栈实现一个队列Myqueue这个类来说,它的两个成员变量是Stack 类型,这也就是自定义类型的,我们光定义出类,不写构造函数,我们创建出一个对象看结果:
?我们可以看到,并没有写Myqueue的构造函数,但是他初始化成功了,原因是他的成员函数是Stack类型,我们给Stack类写出了他的构造函数,对于自定义类型的Myqueue来说,编译器自己给Myqueue生成一个默认的构造函数调用。
~Stack()
{
delete[] _a;
_a = nullptr;
_top = _capacity = 0;
}
程序运行完就变成
?
同样他也是对内置类型需要自己写,自定义类型编译器自动会生成他的默认析构函数
?还是对于Myqueue这类来说可以满足,我们没有写他的构造函数,只给出了Stack类。如图初始化成功
?等到程序运行结束的时候,释放资源
class Stack
{
public:
Stack(int capacity = 4)
{
_a = new int[capacity];
_top = 0;
_capacity = capacity;
}
~Stack()
{
delete[] _a;
_a = nullptr;
_top = _capacity = 0;
}
void Push(const int& data)
{
_a[_top] = data;
_top++;
}
private:
int* _a;
int _capacity;
int _top;
};
int main()
{
Stack st1;
st1.Push(1);
st1.Push(2);
st1.Push(3);
Stack st2(st1);
return 0;
}
我们没有写他的拷贝构造函数,我们在入栈3个数之后用st1拷贝构造一个st2运行,
?
?可以看到直接程序中断了,什么原因呢?
s1对象构造函数创建,在构造函数中,默认申请了4个元素的空间,然后里面存了3个元素1,2,3;s2对象使用s1拷贝构造,而Stack并没有显示定义拷贝构造函数,则编译器会给Stack生成一个默认的拷贝构造函数,默认拷贝构造函数是按照值拷贝的,即将s1中的内容原封不动的拷贝到s2中。因此s1和s2指向了同一块内存空间。当程序运行结束时,s1和s2要销毁,s2先销毁,s2销毁调用析构函数,已经将指向的那一块空间释放了,s1并不知道,再次销毁,再再次释放那一块空间,一块空间多次释放,肯定就会崩溃。
所以这种需要开空间的类我们都需要自己写拷贝构造,否则编译器只会浅拷贝,导致程序崩溃。
Stack(const Stack& st)
{
_a = new int[st._capacity];
memcpy(_a, st._a, sizeof(int) * st._top);//把st1中的数据拷贝
_top = st._top;
_capacity = st._capacity;
}
我们自己写了拷贝构造之后,再运行程序就可以正常啦。
所以,对于需要开空间的类,切记需要自己去实现拷贝构造函数!!!
class Stack
{
public:
Stack(int capacity = 4)
{
_a = new int[capacity];
_top = 0;
_capacity = capacity;
}
~Stack()
{
delete[] _a;
_a = nullptr;
_top = _capacity = 0;
}
Stack(const Stack& st)
{
_a = new int[st._capacity];
memcpy(_a, st._a, sizeof(int) * st._top);
_top = st._top;
_capacity = st._capacity;
}
void Push(const int& data)
{
_a[_top] = data;
_top++;
}
private:
int* _a;
int _capacity;
int _top;
};
int main()
{
Stack st1;
st1.Push(1);
st1.Push(2);
st1.Push(3);
Stack st2;
st2 = st1;
return 0;
}
我把在st1中插入3个数据1,2,3后创建一个对象st2,再st2=st1;运行我们会发现报错。
?什么原因呢?
1.s1对象调用构造函数创建,在构造函数中,默认申请了4个元素的空间,然后存了3个元素1,2,3
2.s2对象调用构造函数创建,在构造函数中,默认申请了4个元素的空间,没有存储元素
3.由于Stack没有显式实现赋值运算符重载,编译器会以浅拷贝的方式实现一份默认的赋值运算符重载即只要发现Stack的对象之间相互赋值,就会将一个对象中内容原封不动拷贝到另一个对象中4.s2= s1;当$1给s2赋值时,编译器会将$1中内容原封不动拷贝到s2中,这样会导致两个问题:
(1)s2原来的空间丢失了,存在内存泄漏;(2)s1和s2共享同一份内存空间,最后销毁时会导致同一份内存空间释放两次而引起程序崩溃。
所以和拷贝构造函数一样,赋值运算符重载也需要对开空间申请资源的类自己实现。
Stack operator=(const Stack& st)
{
_a = new int[st._capacity];
memcpy(_a, st._a, sizeof(int) * st._top);
_top = st._top;
_capacity = st._capacity;
return *this;
}
接下来我们再运行就可以啦。
其它的运算符我们都需要自己实现,因为赋值运算符这类是每个编译器都自动写好的(除了需要开空间),后面我们需要什么运算符就需要自己去写,以日期类为例,我们如果比较两个日期的大小这个规则是我们自己定义的。下面我随便写几个运算符举例
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day;
}
bool operator ==(const Date& d)const
{
if (_year == d._year && _month == d._month && _day == d._day)
{
return true;
}
else
return false;
}
bool operator !=(const Date& d)const
{
if (*this == d)
{
return false;
}
else
return true;
}
bool operator <(const Date& d)const
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year && _month < d._month)
{
return true;
}
else if (_year == d._year && _month == d._month && _day < d._day)
{
return true;
}
else
return false;
}
bool operator <=(const Date& d)const
{
if (*this < d || *this == d)
{
return true;
}
else
return false;
}
bool operator >(const Date& d)const
{
if (!(*this <= d))
{
return true;
}
else
return false;
}
bool operator >=(const Date& d)const
{
if (!(*this < d))
{
return true;
}
else
return false;
}
private:
int _year;
int _month;
int _day;
};
class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ;
int _month ;
int _day ;
};