嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的passion。准备好和我一起冲进代码的奇幻宇宙了吗?Let's go!
我的博客:yuanManGan
我的专栏:C++入门小馆 C言雅韵集 数据结构漫游记 闲言碎语小记坊 题山采玉
目录
这里我们就先只介绍四个简单的默认成员函数
1.类的默认成员函数:
一个类,如果你没有显式定义默认成员函数的话,编译器会自动调用下面这几个默认成员函数。
我们需要了解到
第一:我们未显式定义时,系统给我们定义的函数的行为是什么,是否满足我们的需求。
第二:如果默认生成的函数不满足我们的需求,我们该怎么写一个满足我们需求的默认成员函数。
2.构造函数:
构造函数的作用不是构造空间,而是将类中的成员变量初始化,在定义类变量时,即栈帧创建时以及创建了。
我们的构造函数需要满足以下特点:
1.函数名与类名相同
2.函数无返回值,注意这里是无返回值,不写返回值,不写void
当然构造函数也可以函数重载
#include<iostream>
using namespace std;
class Date
{
public:
//构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
//全缺省
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(2025, 3, 11);
d1.print();
d2.print();
return 0;
}
在使用传参实现构造时,要加上括号和要传入的参数,但如果不需要传参时,我们不能只要括号不要参数,会让人误解这是
Date d1();
全缺省和无缺省以及默认函数不能同时存在,会出现歧义。
大多数的类都需要我们自己写函数都需要我们写构造函数,确定初始化方式。
如果我们不写,编译器对内置类型的初始化没有要求,就是说是否进行初始化是不确定的。
而对应自定义类型做成员变量时,如果该成员变量没有构造函数,编译器就会报错。
少数Myqueue类型会自动调用内置类型里面的构造函数。
#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;
}
// ...
private:
STDataType* _a;
size_t _capacity;
size_t _top;
};
// 两个Stack实现队列
class MyQueue
{
public:
//编译器默认⽣成MyQueue的构造函数调⽤了Stack的构造,完成了两个成员的初始化
private:
Stack pushst;
Stack popst;
};
int main()
{
MyQueue mq;
return 0;
}
3.析构函数
不是对对象本身的销毁,比如局部对象是存在栈帧的,函数结束栈帧销毁,他就释放了。他是完成资源清理工作,类似于destroy功能。一般在我们在类的内部开辟空间时,最后要使用析构函数进行销毁空间。
比如我们之前学习的Date类就不需要析构函数
1.析构函数函数名是在类名前加字符~。
2.无返回值。
3.对象生命周期结束时,系统会自动调用析构函数。
同样Myqueue类型会自动调用内置类型里面的析构函数
#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;
};
// 两个Stack实现队列
class MyQueue
{
public:
//编译器默认⽣成MyQueue的构造函数调⽤了Stack的构造,完成了两个成员的初始化
private:
Stack pushst;
Stack popst;
};
int main()
{
MyQueue mq;
return 0;
}
有了这两个函数我们就不用担心忘记初始化以及销毁了。
4.拷贝构造函数
1.是构造函数的一个重载。
2.拷贝构造函数的第一个参数必须是类类型对象的引用,传值方式编辑器会报错。
Date d2(d1); //传值传参 //传值返回
如果我们不写拷贝构造函数,默认生成的拷贝构造函数会将我们的内置类型进行值拷贝(浅拷贝),对自定义类型去调用它的拷贝构造。
如果我们拷贝构造函数传入的参数写的不是引用类型,会出现死循环。
传值传参要调⽤拷⻉构造
#include<iostream>
using namespace std;
class Date
{
public:
//构造函数
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数
Date(const &Date d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
void Func1(Date d)
{
cout << &d << endl;
d.Print();
}
// Date Func2()
Date& Func2()//野引用
{
Date tmp(2024, 7, 5);
tmp.Print();
return tmp;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024, 7, 5);
// C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,所以这⾥传值传参要调⽤拷⻉
构造
// 所以这⾥的d1传值传参给d要调⽤拷⻉构造完成拷⻉,传引⽤传参可以较少这⾥的拷⻉
Func1(d1);
cout << &d1 << endl;
// 这⾥可以完成拷⻉,但是不是拷⻉构造,只是⼀个普通的构造
Date d2(&d1);
d1.Print();
d2.Print();
//这样写才是拷⻉构造,通过同类型的对象初始化构造,⽽不是指针
Date d3(d1);
d2.Print();
// 也可以这样写,这⾥也是拷⻉构造
Date d4 = d1;
d2.Print();
// Func2返回了⼀个局部对象tmp的引⽤作为返回值
// Func2函数结束,tmp对象就销毁了,相当于了⼀个野引⽤
Date ret = Func2();
ret.Print();
return 0;
}
#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(const Stack& st)
{
// 需要对_a指向资源创建同样⼤的资源再拷⻉值
_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);
if (nullptr == _a)
{
perror("malloc申请空间失败!!!");
return;
}
memcpy(_a, st._a, sizeof(STDataType) * st._top);
_top = st._top;
_capacity = st._capacity;
}
void Push(STDataType x)
{
if (_top == _capacity)
{
int newcapacity = _capacity * 2;
STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_top++] = x;
}
~Stack()
{
cout << "~Stack()" << endl;
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
STDataType* _a;
size_t _capacity;
size_t _top;
};
// 两个Stack实现队列
class MyQueue
{
public:
private:
Stack pushst;
Stack popst;
};
int main()
{
Stack st1;
st1.Push(1);
st1.Push(2);
// Stack不显⽰实现拷⻉构造,⽤⾃动⽣成的拷⻉构造完成浅拷⻉
// 会导致st1和st2⾥⾯的_a指针指向同⼀块资源,析构时会析构两次,程序崩溃
Stack st2 = st1;
MyQueue mq1;
// MyQueue⾃动⽣成的拷⻉构造,会⾃动调⽤Stack拷⻉构造完成pushst/popst
// 的拷⻉,只要Stack拷⻉构造⾃⼰实现了深拷⻉,他就没问题
MyQueue mq2 = mq1;
return 0;
}