【C++】类的6个默认成员函数(超详细)

【C++】类的6个默认成员函数(超详细)

目录

1. 前言

2. 构造函数

概念

特性

实例讲解

1.系统生成构造函数

2.自己如何写构造函数

3.什么时候要写构造函数

3. 析构函数

概念

特性

注意事项

4. 拷贝构造函数

概念

特性

实例讲解

1. 系统默认拷贝构造函数

2.自己实现拷贝构造函数(深拷贝)

使用场景

5. 赋值运算符重载

概念

特性

6. const成员函数&&取地址及const取地址操作符重载

1. 前言

如果一个类中什么成员都没有,简称为空类。

空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员

函数。

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

这是我们定义的一个类

class A

{

};

在我们看来是空的,但经过编译器处理后,会自动生成六个默认的成员函数(这些函数即使我们没有定义,也会显示调用),生成如下

class A

{

public:

A();//构造函数

~A();//析构函数

A(const A& a);//拷贝构造函数

A& operator=(const A& a);//赋值运算符重载

A* operator &();//取地址运算符重载

const A* operator &() const;//const修饰的取地址运算符重载

};

2. 构造函数

概念

构造函数主要完成初始化对象,赋予成员变量一个合适的初始值,相当于C语言阶段写的Init函数。

构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象

特性

1.无返回值

2.构造函数的函数名和类名相同

3.对象实例化时,编译器自动调用对应的构造函数且只调用一次

4.可以重载

实例讲解

1.系统生成构造函数

若没有写构造函数,系统为则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成

但是我们会发现,这里初始化的数据是随机值

这是因为C++编译器在系统默认构造函数中初始化类型时,对内置类型不做处理,对自定义类型会调用它的默认构造函数

解答:C++把类型分成内置类型和自定义类型。内置类型就是语言提供的数据类

型,如:int/float/char...,自定义类型就是我们使用class/struct/union等自己定义的类型

下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员

函数。

但难道我们就不能让系统默认定义内置类型吗,并不是

C++11中针对内置类型不处理初始化为随机值的问题,打了补丁:内置类型成员变量在类中声明可以给默认值,甚至可以给动态开辟的缺省值,缺点是不能判断空间是否开辟成功。

注意这里的默认值是缺省值,不是初始化。初始化是要等对象调用时才叫初始化。

2.自己如何写构造函数

注意:必须要有一个且仅有一个默认构造函数(否则存在二义性)

默认构造函数:无参的构造函数或全缺省的构造函数

当我们没有写的构造函数中没有默认构造函数时,编译器会报错,如下面程序

这时我们就要创建一个默认构造函数了,如下面程序

class Date

{

public:

Date(int year=1,int month=1,int day=1)//方法一:提供全缺省参数的构造函数

{

_year=year;

_month = month;

_day = day;

}

//Date()//方法二:提供无参数的构造函数(两种方法二选一,否则存在二义性)

//{

// _year = 1;

// _month = 1;

// _day = 1;

//}

private:

int _year;

int _month;

int _day;

};

int main()

{

Date d;

return 0;

}

如果我们写了两个默认构造函数,编译器也会报错,如下图

3.什么时候要写构造函数

判断当前这个类是否要写构造函数,主要根据当前类的成员变量类型决定的

若有内置类型,需要写,若无内置类型,则可以不写

3. 析构函数

概念

与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。资源包括动态开辟的空间,文件的关闭等。相当于C语言阶段写的destroy函数。

特性

1. 析构函数名是在类名前加上字符 ~

2. 无参数无返回值类型

3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。

4. 对象生命周期结束时,C++编译系统系统自动调用析构函数

注意事项

1.析构函数不能重载

2.对于编译器生成的默认析构函数,对自定义类型调用他的析构函数。对于内置类型,销毁时不需要资源清理,最后系统直接将其内存回收即可。

3.如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如

Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如栈(Stack)类

4. 拷贝构造函数

概念

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

拷贝构造函数的函数名和构造函数相同,无返回值,在参数上和构造函数构成重载。

特性

1.拷贝构造函数是构造函数的一个重载形式2.拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用3.若未显式定义,编译器会生成默认的拷贝构造函数

实例讲解

1. 系统默认拷贝构造函数

默认的构造函数对于内置类型按照字节拷贝。对于自定义类型则调用它的拷贝构造函数

类中如果没有涉及资源申请时(如栈),默认拷贝拷贝构造函数是可以直接使用的;一旦涉及到资源申请时,则拷贝构造函数是一定要自己写的,否则就是浅拷贝

浅拷贝:通过默认的拷贝构造函数构造的对象,按字节完成拷贝。这种拷贝被称为浅拷贝(值拷贝)

系统默认生成的拷贝构造函数就是浅拷贝

浅拷贝其实就是简单的赋值,如下图(没有自己写拷贝构造函数)

栈的浅拷贝

指针虽然复制了,但是所指向的内容却没有复制,s1和s1指向同一个地址,当main函数生命周期结束时,这两个对象均会发生一次析构,d2先析构,d1后析构,析构时存在同一块空间被释放两次的问题。需要进行深拷贝。深拷贝的拷贝构造函数必须自己手动实现。

2.自己实现拷贝构造函数(深拷贝)

栈这个类因为成员变量中有动态开辟的空间,所以要用深拷贝。如下列代码

class Stack

{

public:

Stack(int capacity = 4)//构造函数

{

_capacity = capacity;

_top = 0;

_arr = (int*)malloc(sizeof(int) * 4);

if (_arr == nullptr)

{

perror("malloc fail");

exit(-1);

}

}

Stack(const Stack& st)//深拷贝(拷贝构造函数)

{

_capacity = st._capacity;

_top = st._top;

_arr = (int*)malloc(sizeof(int) * st._top);

if (_arr == nullptr)

{

perror("malloc fail");

exit(-1);

}

memcpy(_arr, st._arr, sizeof(int) * st._top);

}

~Stack()//析构函数

{

_capacity = 0;

_top = 0;

free(_arr);

_arr = nullptr;

}

private:

int* _arr;

int _top;

int _capacity;

};

int main()

{

Stack s1;

Stack s2(s1);

}

使用场景

1、使用已存在的对象创建新对象

2、函数参数类型为类的类型对象(传值调用,实参拷贝给形参)

3、函数返回值类型为类的良心对象(传值返回)

5. 赋值运算符重载

概念

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数名字为:关键字operator后面接需要重载的运算符符号(如operater==) 函数原型:返回值类型 operator操作符(参数列表) (如bool operater==(const Date& d) )

特性

1.

作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐

藏的this

2.重载操作符必须有一个类类型参数(赋值重载函数必须在类里)

3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义

4.不能通过连接其他符号来创建新的操作符:比如operator@

5. .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

6. const成员函数&&取地址及const取地址操作符重载

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容!

Date* operator&()

{

return this;

}

const Date* operator&()const

{

return this;

}

相关推荐