wordpress伪静态大学,seo诊断网站免费诊断平台,做网站如何与美工配合,福州公司网站C入门之类和对象(中) 文章目录 C入门之类和对象(中)1. 类的6个默认对象2. 构造函数2.1 概念2.2 特性2.3 补丁 3. 析构函数3.1 概念3.2 特性3.3 总结 4. 拷贝构造函数4.1 概念4.2 特性4.3 总结 1. 类的6个默认对象
如果一个类中什么都没有#xff0c;那么这个类就是一个空类。…C入门之类和对象(中) 文章目录 C入门之类和对象(中)1. 类的6个默认对象2. 构造函数2.1 概念2.2 特性2.3 补丁 3. 析构函数3.1 概念3.2 特性3.3 总结 4. 拷贝构造函数4.1 概念4.2 特性4.3 总结 1. 类的6个默认对象
如果一个类中什么都没有那么这个类就是一个空类。但是任何类如果什么都不写的话编译器会自动生成6个默认成员函数
默认成员函数用户没有显式实现用户没有写编译器自动生成的成员函数被称为默认成员函数
class Data{};2. 构造函数
2.1 概念
假设有以下类
#include iostream
using namespace std;
class Date
{
public:void Init(int year 2024, int month 4, int day 15){_year year;_month month;_day day;}void Print(){cout _year - _month - _day endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Init();d1.Print();Date d2;d2.Init(2024, 5, 1);d2.Print();return 0;
}上述类中是使用Init函数对类进行初始化Init使用全缺省参数如果没有传值的话使用缺省值初始化但是对于这这种类就算不初始化也不会有什么问题但是对于顺序表链表等如果不初始化就会报错往往我们会容易忘记调用初始化这时候构造函数就派上用场了
构造函数是一种特殊的函数名字与类名相同没有返回值在默认成员函数中没有返回值指的都是不写在创建类对象时由编译器自动调用保证每个成员都有一个初始值并且在类对象整个生命周期只会调用一次
2.2 特性
构造函数是一种特殊的函数构造函数并不是用于开辟空间创建对象而是为对象进行初始化 特征 函数名与类名相同函数没有返回值不写返回值对象实例化时编译器会自动调用构造函数可以重载可以根据需求写多个初始化方式如果类中没有显式定义构造函数没有写编译器就会自动生成一个无参的构造函数反之编译器则不会生成由编译器生成的无参构造函数,不会对类中的内置类型int char等等进行处理但是对调用类中自定类型class struct union等等的构造函数如果类中自定类型还是没有则也不处理 C中没有规定对自定类型class struct union等等初始化成0或者其他取决于编译器的实现无参构造函数和全缺省的构造函数由编译器自动生成构造函数都可以被称为默认参构造函数但是默认参构造函数只能存在一个 示例1:
#include iostream
using namespace std;
class Date
{
public://Date(int year, int month, int day) 这种写法会报错这种不是默认的构造函数Date(int year 2024, int month 4, int day 15){_year year;_month month;_day day;}void Print(){cout _year - _month - _day endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;//调用全缺省的构造函数2024-4-15d1.Print();return 0;
}示例2
#include iostream
using namespace std;
class Time
{
private:int _hour;int _minute;int _second;
};
class Date
{
public:void Print(){cout _year - _month - _day endl;}
private:int _year;int _month;int _day;Time a;
};int main()
{Date d1;d1.Print();return 0;
}示例3
#include iostream
using namespace std;
class Date
{
public:Date(){}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;d1.Print();return 0;
}存在多个构造函数报错
2.3 补丁
由于不对内置类型进行初始化所以在C 11中打了一个补丁允许内置成员在声明时可以给一个默认值
#include iostream
using namespace std;
class Date
{
public:void Print(){cout _year - _month - _day endl;}
private:int _year 2024;int _month 4;int _day 15;
};int main()
{Date d1;d1.Print();return 0;
}用声明时的默认值初始化2024-4-15 总结 一般情况下构造函数都要由我们自己实现少部分情况下可以不用实现如果类中只有自定类型而这个自定类型内部存在构造函数例如MyQueue 3. 析构函数
3.1 概念
析构函数是与构造函数相反的一种特殊函数析构函数不是对对象进行销毁局部变量的销毁是由编译器处理的而是析构函数是对对象中资源的清理且会在对象销毁时自动调用
3.2 特性 在类名前面加上~无参数无返回值不写返回值一个类只有一个析构函数如果没有显式定义没有写则编译器会自动生成默认析构函数由于没有参数析构函数不能重载在对象生命周期结束时编译器会自动调用析构函数与构造函数相似的是析构函数不会对内置类型进行处理对自定类型则是调用其析构函数 示例
#include iostream
using namespace std;
class Stack
{
public:Stack(int n 4){cout Stack() endl;int* tmp (int*)malloc(sizeof(int) * n);if (nullptr tmp){perror(malloc fail);return;}_arr tmp;_capacity n;_size 0;}void Push(int x){//扩容_arr[_size] x;_size;}~Stack(){cout ~Stack() endl; //方便查看if (_arr) //防止被多次销毁加个判断{free(_arr);_arr nullptr;_capacity 0;_size 0;}}
private:int* _arr;int _size;int _capacity;
};
int main()
{Stack s;s.Push(1);s.Push(2);s.Push(3);s.~Stack();return 0;
}析构函数也是可以显式调用的 3.3 总结
在没有需要资源清理的时候可以不写析构函数 a. 如Date类没有需要清理的内置类型 b.没有需要清理的内置类型剩下的其他自定类型中存在析构函数如MyQueue也不需要写析构函数 2.有资源清理就要写析构函数如StackList
4. 拷贝构造函数
4.1 概念
拷贝构造函数只有一个形参该形参为本类型对象的引用一般会使用const修饰在用已经存在的类类型对象时创建新对象时会由编译器自动调用
4.2 特性
是构造函数的一种重载形式函数名与类型一致拷贝构造函数的参数只能有一个且得是类类型对象的引用否则在使用拷贝构造函数会直接报错引发无穷递归调用如果没有显式定义编译器会自动生成默认的拷贝构造函数默认的拷贝构造函数会对内置类型进行处理按内存存储按字节序完成拷贝也被称为浅拷贝或者值拷贝
示例1
#include iostream
using namespace std;
class Date
{
public:Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}void Init(int year 2024, int month 4, int day 15){_year year;_month month;_day day;}void Print(){cout _year - _month - _day endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024,4,15);Date d2 d1; //与下面创建对象d3是等价的两种写法d2.Print();Date d3(d1);d3.Print();return 0;
}示例2
#include iostream
using namespace std;
class Stack
{
public:Stack(int n 4){cout Stack() endl;int* tmp (int*)malloc(sizeof(int) * n);if (nullptr tmp){perror(malloc fail);return;}_arr tmp;_capacity n;_size 0;}void Push(int x){//扩容_arr[_size] x;_size;}~Stack(){cout ~Stack() endl;if (_arr){free(_arr);_arr nullptr;_capacity 0;_size 0;}}
private:int* _arr;int _size;int _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);Stack s2 s1;return 0;
}代码运行结果 报错 编译器生成的默认拷贝构造是不够用在上述代码中s2对象使用s1对象的拷贝由于是浅拷贝会将s1中的内容原封不动的拷贝给s2因此s1和s2调用的是同一块空间在调用析构函数时s1将空间释放了但是s2中存放的还是s1的空间因为还会再释放一次一块内存空间的多次释放会造成程序奔溃。同时在对任意一个栈中push数据的时候另一个栈中的size是不会加的但是共用的是同一块空间数据丢失等等问题
示例3错误写法
#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(Date d) //错误写法 会引发无穷递归{_year d._year;_month d._month;_day d._day;}
private:int _year;int _month;int _day;
};Date Func(Date d)
{Date tmp(d);return tmp;
}int main()
{Date d1(2024, 4, 15);Func(d1);return 0;
}在返回一个局部变量时由于局部变量出作用域就销毁了所以会将局部变量拷贝给一个临时变量在给拷贝给临时变量时又会调用拷贝构造函数在调用拷贝构造函数时又会将返回值拷贝给一个临时变量造成无穷递归 4.3 总结
在类中如果没有涉及需要资源管理的内置类型是可以不写拷贝构造函数的编译器自动生成的浅拷贝就够用但是一旦涉及就需要自己实现拷贝构造函数了拷贝构造函数的参数只有一个且必须是类类型对象的引用使用传值方式编译器直接报错因为会引发无穷递归调用