构造函数
含义:在对象被创建时使用特定的值构造对象,将对象初始化为一个特定的初始状态
构造函数的形式:
1.函数名与类名相同
2.不能定义返回值类型,不能有return语句
3.可以有形式参数,也可以没有
4.可以是内联参数
5.可以重载
6.可以带默认值参数
构造函数的调用时机:
对象创建时自动调用,例如Clock myClock(0,0,0);
不写构造函数时由编译器调用默认构造函数(隐含生成的构造函数):
- 参数列表为空,不为数据成员设置初始值
- 如果类内定义了成员的初始值,则使用类内定义的初始值
- 如果类内没有定义成员的初始值,则以默认方式初始化
- 基本类型的数据默认初始化是不确定的
-
默认构造函数:
调用时可以不需要实参的构造函数:
- 参数表为空
- 全部参数都有默认值
- 下面两个都是默认构造函数,如果在类内同时出现会报错:
Clock();
Clock(int newH=0,int newM=0,int newS=0);
class Clock{
public:
Clock(int newH,int newM,int newS);
Clock();
void setTime(int newH,int newM,int newS);
void showTime();
private:
int hour,minute,second;
}
Clock::Clock(int newH,int newM,int newS):hour(newH),minute(newM),second(newS){
}
Clock::Clock():hour(0),minute(0),second(0){
}
int main(){
Clock c(0,0,0);
c.showTime();
return 0;
}
“=default”
如果类中已定义构造函数,此时仍希望编译器生成默认构造函数,可以使用=defalut
class Clock{
public:
Clock()=defalut;
Clock(int newH,int newM,int newS);
private:
int hour,minute,second;
}
- default的作用是如果主函数中创建对象时都给了参数,也就是不需要默认构造函数,那默认构造函数在整个程序运行中就不生成,一旦存在一个对象创建时没给函数,此时就会生成默认构造函数,与Clock();的区别是Clock();这个默认构造函数在整个程序运行中都是存在的,因此default一定程度上可以节约空间
-
委托构造函数:
含义:类中往往有多个构造函数,只是参数表和初始化列表不同,其初始化算法都是相同的,这时为了避免代码重复,可以使用委托构造函数
委托构造函数使用类的其他构造函数执行初始化过程
Clock::Clock():Clock(0,0,0){}
复制构造函数:
1.我们经常会需要用到一个已经存在的对象去初始化一个新的对象,类似于int a=1,int b=a,这时就需要复制构造函数
2.隐含生成的复制构造函数可以实现对应数据成员一一复制
3.自定义的复制构造函数可以实现特殊的复制功能
定义:
复制构造函数是一种特殊的构造函数,其形参为本类的对象引用,作用是用一个已存在的对象去初始化同类型的对象
class 类名{
public:
类名(形参);
类名 (const 类名 &对象名);
...
}
类名::类(const 类名 &对象名){函数体}
“=delete”
如果要求一个类中的对象相互之间不能通过复制来赋值,也就是不允许复制,那么可以用=delete指示编译器不生成默认复制构造函数
class Point{
public:
Point(int xx=0,int yy=0){x=xx;y=yy;}
Point(const Point &p)=delete;
private:
int x,y;
}
复制构造函数被调用的三种情况:
1.定义一个对象时,以本类另一个对象作为初始值,发生复制构造
2.如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造
3.如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主调函数,发生复制构造
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/74ead4ea89d1f3a3d836e4860c37b2a8.png)
class Point{
public:
Point(int xx=0,int yy=0){x=xx;y=yy;}
Point(const Point &p);
void setX(int xx){x=xx;}
void setY(int yy){y=yy;}
int getX() const{return x;}
int getY() const{return y;}
private:
int x,y;
}
Point::Point(const Point &p){
x=p.x;
y=p.y;
cout<<"Calling the copy constructor"<<endl;
}
左值和右值:
- 左值是位于赋值运算左侧的对象或变量,右值是位于赋值运算右侧的值
左值引用和右值引用:
- 对持久存在变量的引用称为左值引用,用&表示
- 对短暂存在可被移动的右值的引用称为右值引用,用&&表示
- 这里的&不是地址符
float n=6;
float &lr_n=n;
float &&rr_n=n;
float &&rr_n=n*n;
- 通过标准库< utility >中的move函数可将左值对象移动为右值
float n=10;
float &&rr_n=std::move(n);
移动构造函数(了解即可):
基于右值引用,移动构造函数通过移动数据方式构造对象,与复制构造函数类似,移动构造函数参数为该类对象的右值引用,区别在于复制后是两个对象,移动后新对象存在,旧对象消失
#include<utility>
class astring
public:
std::string s;
astring(astring&& o)noexcept:s(std::move(o.s))
{函数体}
- 移动构造函数不分配新内存,理论上不会报错,为配合异常捕获机制,需声明noexcept表面不会抛出异常
- 被移动的对象不应再使用,需要销毁或重新赋值
-
析构函数
- 完成对象被删除前的一些清理工作
- 在对象的生存期结束的时刻系统自动调用它,然后再释放此对象所属的空间
- 如果程序中未声明析构函数,编译器将自动产生一个默认的析构函数,其函数体为空
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/8bac91bab4ab0c77914eb26acb533896.png)