目录
前言
今天我们来学几个默认成员函数
类与对象(中)
什么是默认成员函数?
在C++当我们不去写,编译器会默认生成几个成员函数,他们各有各的作用,当我们写了,编译器就不会自动生成。
当然,默认生成的成员函数有利有弊,好处就是方便不用我们自己去实现,坏处更明显,有时候默认生成的并不能满足我们的需求,因此接下来我们需要学习这几个默认构造函数来加深我们对C++这门语言的理解
1.构造函数
我们常常说名字要和实际意义相符,而这个构造函数显然并不是起到我们想象中的作用
简单来说,构造函数用于对象的初始化,是必不可少的
构造函数的特点:
(1)构造函数可以重载
(2)无参构造函数,完全缺省构造函数以及编译器自己生成的构造函数都叫做默认构造函数,但是只能有且只能存在一种。
(3)构造函数在类的对象实例化时自动调用完成初始化,因此不开辟空间
(4)构造函数在初始化自定义类型时需要调用默认构造函数,需要我们自己去写这个函数
(5)函数名与类名相同,没有返回值
#include<iostream>
using namespace std;
class Date{
public:
//无参的
Date()
{
_year = 1;
}
//全缺省的
Date(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
2.析构函数
析构函数听名字我们猜不到这个成员函数具体的作用,简单来说这个函数的作用与构造函数正好相对,他负责对象的清理;
析构函数的特点:
(1)析构函数名是类名前加一个 ~ ,同样没有返回值,无参数;
(2)生命周期结束,自动调用析构函数;
(3)c++规定,在局部域中,先创建的后析构;
如果我们的类中没有需要开辟资源的,那么我们可以直接使用编译默认的析构函数,如果存在,那就需要我们自己去实现一个析构函数
它就相当于我们在学数据结构时,栈和队列的销毁的那个函数,有了这个函数,我们就不需要在主动调用,就不会出现当我们忘记释放这块空间的时候,面临着内存泄漏的风险,
同样析构函数在函数周期结束后就会自动调用
析构函数的基础形式需要在类名前加一个 “ ~ ” 符号, 对于不开辟资源空间的对象,没有需要释放和销毁的点,因此默认析构函数也没有什么作用,不释放资源我们可以不写析构函数
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
Stack(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a)
{
perror("malloc申请空间失败");
return;
}
_capacity = n;
_top = 0;
}
~Stack()
{
cout << "~Stack()" << endl;
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
STDataType* _a;
size_t _capacity;
size_t _top;
}
3.拷贝构造
拷贝构造比较好理解,就像他的名字,将里一个对象拷贝给另一个创建好的新对象
拷贝构造其实是一种特殊的构造函数,是构造函数的一个重载
拷贝构造的特点:
(1)形参必须引用调用,否则启发无穷递归,编译器报错
第一个形参必须是类类型的引用,可以有多个参数,但后面的形参必须有缺省值
(2)c++规定对自定义类型进行拷贝行为必须调用拷贝构造,自定义类型的传值传参和传值返回必须调用拷贝构造函数
(3)编译器生成的默认拷贝构造是浅拷贝,也就是一个字节一个字节的拷贝,如果我们创建的是自定义,那么浅拷贝可能会出现一些问题,就需要我们自己去实现深层拷贝;
比如说,我们实现栈这样的一个数据结构,浅拷贝就可能会导致两个指针指向同一块空间。
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
4.运算符重载
<1>
运算符重载,我们在使用基本类型的时候常常会用到这些基本符号,比如说 “-”,“=”,“<”,“>”,“+”等等
但是当我们创建了两个类类型的对象时,编译器肯定不允许我们去这么用,所以需要我们自己去定义重载实现,那这个函数就叫做运算符重载
运算符重载可以定义在全局域中也可以在类中,一般我们都将它定义在类中
类名 operator 运算符(参数)
{
定义
}
这就是运算符重载的基本形式,而运算符是几元,就意味着需要几个参数,当然我们不要忽略掉this指针。
注意: .* :: sizeof ? : . 以上5个运算符不能重载。
<2>赋值运算符重载
我们要将赋值运算符与拷贝构造区分开,赋值运算符是将已经初始化的对象进行赋值,而拷贝构造是对刚创建的对象进行拷贝。
另外为了区分前置++和后置++,我们通过调用的时候有没有参数来区别他
Date& operator++()
{
cout << "前置++" << endl;
//...
return *this;
}
5.取地址运算符重载
(1)const引用
int main{
const Date d1;
d1.print();
return 0;
}
当我们去编译上面这段代码的时候是会报错的,因为成员函数里面this指针完整的形式是这样的
Date const this
this不能改变,而传过来的指针是可以改变的,这涉及到了权限的放大,我们之前几章和大家分享过,权限只能缩小或者相等,那我们该怎么做呢?
我们可以在成员函数的后面加个const。
需要注意的是:不是所有的成员函数都可以加const,比如说引用传参,对传过来的对象进行修改的的就不能随意的加上const。
(2)取地址运算符重载
&这个编译器默认是this指针的地址
总结
今天,我们对类和对象又有了进一步的了解