福建:网站建设,招聘网站开发设计,青岛做网站的好公司,h5页面制作软件电脑版类和对象#xff08;上#xff09;一#xff0c;构造函数1#xff0c;概念2#xff0c;特性二#xff0c;析构函数1#xff0c;概念2#xff0c;特性三#xff0c;拷贝构造1#xff0c;概念2#xff0c;特性四#xff0c;运算符重载1#xff0c;概念2#xff0c;…
类和对象上一构造函数1概念2特性二析构函数1概念2特性三拷贝构造1概念2特性四运算符重载1概念2赋值运算符重载五重载与const 重载六总结一构造函数
1概念
class Stack
{
public:void Init(int n 4){_a (int*)malloc(sizeof(int) * n);if (!_a){perror(malloc fail);exit(-1);}_top 0;_capcity n;}void Destroy(){if (_a){free(_a);_a nullptr;_top _capcity 0;}}
private:int* _a;int _top;int _capcity;
};int main()
{Stack st;st.Init();return 0;
}由于C的类内允许定义函数但是像上述那样需要我们去手动的初始化对象很容易玩掉。于是C中提出了构造函数这一概念。 构造函数 构造函数是一个特殊的成员函数名字与类名相同在类的实例化时由编译器自动调用以保证每个数据成员都有一个合适的初始值并且在对象的整个生命周期内只调用一次。 注意构造函数不是用来创建对象的而是用来初始化对象的构造函数是没有返回值的且支持函数重载。
所以一般在C中是这样写的
class Stack
{
public:Stack(int n 4){_a (int*)malloc(sizeof(int) * n);if (!_a){perror(malloc fail);exit(-1);}_top 0;_capcity n;}
private:int* _a;int _top;int _capcity;
};
int main()
{Stack st1;Stack st2(8);return 0;
}切忌这样使用无参的构造函数
Stack st3();这会与函数的声明冲突函数名为st3参数为空返回值为Stack的函数声明。
2特性
函数名与类名相同无返回值类的实例化时编译器自动调用其构造函数支持函数重载如果没有显示的定义构造函数编译器会自动生成一个默认构造函数一旦用户显示定义了构造函数那么编译器就不会自动生成编译器自动生成的默认构造函数对内置类型不做处理对自定义类型会去调用其默认构造函数
class Queue
{
public:private:Stack st1;Stack st2;int _size;
};
int main()
{Queue q;return 0;
}即使Queue这个类没有显示定义构造函数所以编译器自动生成了一个默认构造函数对自定义类型会去调用其默认构造对内置类型不做处理可以看到编译器也对_size进行了初始化这只是针对本编译器会这样做不具有普遍性。 针对编译器自动生成的默认构造函数对内置类型不做处理在C 11中提出允许给非静态成员变量提供一个缺省值即在类的实例化时未对某个成员变量初始化那么其的值就为所给的缺省值。
class Queue
{
public:private:Stack st1;Stack st2;int _size 1;
};
int main()
{Queue q;return 0;
}无参的构造函数与全缺省的构造函数与编译器自动生成的都是默认构造函数且默认构造函数只允许有一个。
二析构函数
1概念
void Destroy()
{if (_a){free(_a);_a nullptr;_top _capcity 0;}
}与构造函数的功能相反当我们定义的对象涉及到动态内存开辟时之前我们会手动的去调用Destroy函数去进行资源管理析构函数在对象销毁的时候自动调用完成对象中资源的清理工作且函数名为 ~类名 没有参数不支持函数重载。 class Stack
{
public:Stack(int n 4){_a (int*)malloc(sizeof(int) * n);if (!_a){perror(malloc fail);exit(-1);}_top 0;_capcity n;}~Stack(){if (_a){free(_a);_a nullptr;_top 0;_capcity 0;}}
private:int* _a;int _top;int _capcity;
};2特性
函数名 ~类名无参数不支持函数重载对象销毁时自动调用当用户没有显示的去写析构函数时编译器会自动生成一个析构函数。编译器自动生成的析构函数对内置类型不做处理对于自定义类型会去调用其析构函数。
class Stack
{
public:Stack(int n 4){_a (int*)malloc(sizeof(int) * n);if (!_a){perror(malloc fail);exit(-1);}_top 0;_capcity n;}private:int* _a;int _top;int _capcity;
};
void test1()
{Stack st1(6);
}
int main()
{test1();return 0;
}Stack 类中的成员变量都是内置类型编译器自动生成的析构函数不对其做处理导致_a指向的空间没有被释放造成内存泄露。 如果类中没有涉及申请资源时析构函数可以不用写直接使用编译器自动生成的析构函数像Stack类这样的涉及申请资源时一定要写析构函数。
三拷贝构造
1概念 拷贝构造函数是构造函数的一种重载形式只有一个参数是本类对象的引用一般用const 修饰用已有对象去初始化创建新的对象时会自动调用拷贝构造。 2特性
拷贝构造函数是构造函数的一种重载形式拷贝构造函数的参数只有一个且必须是同类对象的引用使用传值的方式编译器会报错因为会发生无穷递归。 如果调用拷贝构造使用传值的方式由于传值传参会发生拷贝构造也就是说我们还没调用到拷贝构造之前要现发生一次拷贝构造依次反复无穷的递归下去。如果没有显示的定义拷贝构造函数编译器会自动生成一个拷贝构造。其对内置类型进行简单的值拷贝对自定义类型会调用其拷贝构造函数。
class Date
{
public:Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2023, 2, 10);Date d2(d1);return 0;
}当类中涉及到资源的申请时一定也要写显示的拷贝构造函数否则自动生成的是浅拷贝。
class Stack
{
public:Stack(int n 4){_a (int*)malloc(sizeof(int) * n);if (!_a){perror(malloc fail);exit(-1);}_top 0;_capcity n;}~Stack(){if (_a){free(_a);_a nullptr;_top 0;_capcity 0;}}
private:int* _a;int _top;int _capcity;
};
int main()
{Stack st1;Stack st2(st1);return 0;
}可以看到如果使用把编译器提供的拷贝构造会发生浅拷贝会导致一系列错误的出现。 Stack(const Stack st){if (this ! st){_a (int*)malloc(sizeof(int) * st._capcity);if (!_a){perror(malloc fail);exit(-1);}memcpy(_a, st._a, sizeof(int) * st._capcity);_top st._top;_capcity st._capcity;}}四运算符重载
1概念
int main()
{vectorint a(4, 0);for (int i 0; i 4; i){a[i] i;}for (int i 0; i 4; i){cout a[i] ;}return 0;
}vector是STL库中提供的顺序表这一数据结构但是你发现他居然能像数组一样使用[ ]来访问其成员其实这是重载了[ ]这个运算符本质上是函数调用。 运算符重载 C中为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数也具有返回值函数名参数列表且与普通函数类似。其函数名为operator关键字 要重载的运算符
例如我们自己实现一个数组类来运用一下这个运算符重载
class Array
{
public:int operator[](int i){return a[i];}
private:int a[10];int size 0;
};int main()
{Array arr1;for (int i 0; i 5; i){arr1[i] i;}for (int i 0; i 5; i){cout arr1[i] ;}return 0;
}注意
不能通过连接其他符号来创建新的操作符比如operator重载运算符必须有一个参数是自定义类型的用于内置类型的运算符其含义不能改变例如内置的整型不 能改变其含义作为类成员函数重载时其形参看起来比操作数数目少1因为成员函数的第一个参数为隐藏的this.* :: sizeof ?: . 注意以上5个运算符不能重载。
2赋值运算符重载
1运算符重载格式
参数类型const T传递引用可以提高传参效率同时也防止了由于赋值的顺序产生-错误。返回值类型T返回引用可以提高返回的效率有返回值目的是为了支持连续赋值检测是否自己给自己赋值返回*this 要复合连续赋值的含义
2赋值运算符只能重载成类的成员函数不能重载成全局函数 原因赋值运算符如果不显式实现编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载就和编译器在类中生成的默认赋值运算符重载冲突了故赋值运算符重载只能是类的成员函数。
3用户没有显式实现时编译器会生成一个默认赋值运算符重载以值的方式逐字节拷贝。注意内置类型成员变量是直接赋值的而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
class Date
{
public:Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}Date operator[](const Date d){if (this ! d){_year d._year;_month d._month;_day d._day;}}
private:int _year;int _month;int _day;
};五重载与const 重载 这两个默认成员函数一般不用自己实现编译器会默认生成。 class Date
{
public :
Date* operator()
{return this ;}
const Date* operator()const
{return this ;
}
private :int _year ; // 年int _month ; // 月int _day ; // 日
};六总结
1类的6个默认成员函数
构造函数析构函数拷贝构造函数赋值运算符重载运算符重载const 运算符重载
2默认成员函数共同的性质就是用户不主动提供时编译器会自动提供默认成员函数。 3编译器自动提供的构造函数与析构函数对内置类型不做处理对与自定义类型会调用其构造函数和析构函数。 4编译器自动提供的赋值运算符重载 拷贝构造对内置类型会进行值拷贝对自定义类型调用其赋值运算符重载和拷贝构造。 5C 11 提供了可以给内置类型提供缺省值。