类的6个默认成员函数
空类
class
{ };//大小为一个字节
构造函数
例:
日期类
class Date
{
public:
void InitDate(int year,int month,int day)
{
_year=year;
_month=month;
_day=day;
}
void PrintDate()
{
cout<<"/"<<day<<"/"<<month<<"/"<<year<<endl;
}
private:
int _year;
int _month;
int _day;
}
int main()
{
Date d;
d.InitDate(2019,3,19)
d.PrintDate();
return;
}
概念
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次。
构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主 要任务并不是开空间创建对象,而是初始化对象
特性
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。
- 构造函数可以重载。
class Date
{
public:
Date()
{
cout<<"Date&()"<<this<<endl;
}
Date(int year,int month,int day)
{
_year=year;//把参数赋值给当前对象的年月日
_month=month;
_day=day;
}
private:
int _year;
int _month;
int _day;
}
int main()
{
Date d4(2019,3,19);//去寻找带有三个参数的构造函数Date
Date d1,d2,d3;//调用无参数的Date构造函数
//有了构造函数便不需要InitDate()初始化函数,编译器每次自动调用
Date d4();//编译不通过,这是一个函数声明,不是创建一个对象
//没有参数,返回一个日期类型对象的函数
}
- 如果类中没有显示定义构造函数,c++编译器会自动生成一个无参的默认构造函数,一旦用户显示定义,编译器将不再生成。
- 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
- 成员变量的命名风格
class Date
{
public:
Date(int year,int month,int day)
{
_year=year;
_month=month;
_day=day;
//m_year=year;
//m_month=month
//m_day=day;
}
private:
int _year;
int _month;
int _day;
//int m_year;
//int m_month;
//int m_day;
};
析构函数
析构函数概念
析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而 对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
特性
- 析构函数名是在类名前面加~
- 无参数无返回值
- 一个类有且只有一个析构函数,若未显示定义,系统会自动生成默认的析构函数
- 对象生命周期结束时,c++自动调用析构函数
typedef int DateType;
class Seqlist
{
public:
Seqlist()
{
_pDate=(DateType*)malloc(capacity*sizeof(DateType));
assert(_pDate);
_size=0;
_capacity=capacity;
}
~Seqlist()
{
if(_pDate)
{
free(_pDate);
_pDate=NULL;
_capaticy=0;
_size=0;
}
}
private:
int* _pDate;
size_t size;
size_t capacity;
};
析构函数不能重载,有参数(编译器自动给的),不能再加参数,而构造函数可以加参数
5. 编译器生成的 默认析构函数,对会自定类型成员调用它的析构函数。
//编译器生成的析构函数
~Seqlist()
{ };
//此时函数里面的数据没有被释放
拷贝构造函数
概念
只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
特征
- 拷贝构造函数是构造函数的一个重载形式;
- 拷贝构造函数的参数只有一个,并且必须使用引用传参,使用传值方式会引发无穷递归调用。
class Date
{
public:
Date(int year,int month,int day)
{
_year=year;
_month=month;
_day=day;
}
Date(const Date& d)
{
_year=d._year;
_month=d._month;
_day=d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(d1);
return ;
}
先创建d1,再创建d2,先销毁d2,再销毁d1.
数据结构 栈:后进先出。
栈区:后进先出,具有栈特性一块内存空间。
若写反的话,如:
d._day=_day;
就会把d中的内容修改掉,为了防止这种情况,一般加const:
Date(const Date &d)
3. 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。
出了函数定义域,要销毁s1,s2,调用析构函数,先释放s2,再释放s1,而s1 ,s2用同一个块空间,s2已经释放这块空间,s1就不能再释放,就会出错。
对于string类必须显示定义拷贝构造函数,而Date类就不用显示,不用提供拷贝构造函数
运算符重载
运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
注意:
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型或者枚举类型的操作数
- 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
- 作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的
- 操作符有一个默认的形参this,限定为第一个形参
- .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
按值返回,按引用返回,按常量引用返回
返回对象涉及到生成返回对象的临时拷贝,这是调用函数的程序可以使用的拷贝。因此,返回对象的时间成本包括调用复制构造函数来生成拷贝所需的时间和调用析构函数删除拷贝所需的时间。返回引用可以节省时间和内存。
函数不能返回在函数中创建的临时对象的引用,因为当函数结束时,临时对象将消失,在这种情况下应返回对象,以生成一个调用程序可以使用的拷贝。
通常的规则是,如果函数返回在函数中创建的临时对象,则不要使用引用。例如,下面的方法使用构造函数创建一个新对象,然后返回该对象的拷贝:
Vector Vector::operator+(const Vector & b)const
{
return Vector(x+b.x,y+b.y);
}
如果返回的是通过引用或指针来传递给它的对象,则应按照引用传递。例如,下面的代码按引用返回调用函数的对象或作为参数传递给函数的对象:
const Stock & Stock::topval(const Stock & s)const
{
if(s.total_val>total_val)
return s;
else
return *this;
}
bool operator==(const Date& d)
{
return _year==d._year&&
_month==d._month&&
_day==d._day;
}
bool operator!=(const Date& d)
{
return !(*this==d);
}
赋值运算符重载
class Date
{
public:
Date(int year,int month,int day)
{
_year=year;
_month=month;
_day=day;
}
Date(const Date& d)
{
_year=d._year;
_month=d._month;
_day=d._day;
}
Date& operator=(const Date& d)
{
if(this!=&d)
_year=d.year;
_month=d._month;
_day=d._day;
}
private:
int _year;
int _month;
int _day;
};
赋值运算符主要有四点:
- 参数类型
- 返回值
- 检测是否自己给自己赋值
- 返回*this
- 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。