析构函数
概念
析构函数是一个特殊的函数,函数名和类名相同,但是要在前面加~,既没有参数,也没有返回值。析构函数在对象被销毁自动调用一次
如果类中没有析构函数,编译器会生成一个什么也不做的析构函数
如果类中有析构函数,编译器不再做该动作
如果类中有类类型成员,先析构自己,在析构成员(先构造的要后析构)
练习: 为mystring类实现析构函数和获取指定位置字符的成员函数
/*06-字符串类的拷贝构造函数*/
#include <iostream>
#include <cstring>
using namespace std;
class mystring{
public:
//构造函数
mystring(const char *s=NULL)
{
if(!s){//没有传参数
this->len = 10;
this->str = new char[this->len];
memset(this->str,0,10);
}
else{
this->len = strlen(s)+1;
this->str = new char[this->len];
strcpy(this->str,s);
}
}
//拷贝构造函数
mystring(const mystring &s)
{
this->len = s.len;
this->str = new char[this->len];
strcpy(this->str,s.str);
}
//析构函数
~mystring()
{
delete[] this->str;
}
//打印字符串
void show()
{
cout<<this->str<<endl;
}
//获取空间大小
size_t get_len()
{
return this->len;
}
//获取字符串长度
size_t get_size()
{
return strlen(this->str);
}
//获取指定位置的字符
char at(size_t n)
{
if(n>=this->len){//越界
return -1;
}
return this->str[n];
}
//修改字符串内容 健壮性
void modify_str(const char *s)
{
if(!s){//为空修改为长度为10的空串
delete[] this->str;
this->len = 10;
this->str = new char[this->len];
memset(this->str,0,10);
}
else{//非空
if(this->len<strlen(s)+1){//空间不够
//调整空间
delete[] this->str;
this->len = strlen(s)+1;
this->str = new char[this->len];
}
strcpy(this->str,s);
}
}
private:
char *str;//字符串内容首地址
size_t len;//空间大小
};
int main()
{
//mystring str1;
mystring str1("welcome to GEC!");
str1.show();
mystring str2 = str1;
str2.show();
str1.modify_str("byebye");
str1.show();
str2.show();
cout<<str1.get_size()<<endl;
return 0;
}
拷贝构造函数
概念
拷贝构造函数是一个特殊的构造函数,使用一个已有对象去初始化一个新建对象时,调用拷贝构造函数。
//A是类类型
A a;//构造函数
A b = a;//拷贝构造函数
A c;
c = a;//不是拷贝构造
实现语法
class A{
public:
A(…){…}//构造函数
/拷贝构造/
A(const A &a){…}
};
拷贝构造函数调用的时机
使用已有的对象去初始化新对象
A a;
A b = a;//拷贝构造
把一个对象传递给本类型的形参
把一个对象作为函数的返回值(编译器会优化成传递构造)
什么情况需要重写拷贝构造函数
如果一个类没有拷贝构造函数,编译器会自动生成一个按逐字节拷贝的拷贝构造函数
如果希望自定义拷贝构造的过程可以重写拷贝构造函数,其实就是对象中存在独立的内存(资源)重写拷贝构造
默认的拷贝构造函数属于浅拷贝
为独立内存重新分配空间,并拷贝空间中的数据就叫深拷贝
拷贝构造是由一个旧对象初始的值去拷贝新对象的值
/*09-字符串类的静态函数*/
#include <iostream>
#include <cstring>
using namespace std;
class date {
public:
date(int year = 2022, int month = 7, int day = 9) {
this->year = year;
this->month = month;
this->day = day;
}
void show() {
cout << "year " << this->year << " month " << this->month << " day " << day << endl;
}
date(const date &dt) {
cout << " date(const date &t)" << endl;
this->year = dt.year;
this->month = dt.month;
this->day = dt.day;
}
~date() { //析构要跟类名相同
cout << "~getdate" << endl;
};
private:
int year;
int month;
int day;
};
int main() {
date dt1(2000, 11, 20);
dt1.show();
date dt2(0000, 0000, 00);
dt2 = dt1;
dt2.show();
//析构函数会在函数结束后执行
return 0;
}
练习:
为mystring类直线拷贝构造函数,并提供获取字符串长度(有效字符个数)的成员函数。
/*06-字符串类的拷贝构造函数*/
#include <iostream>
#include <cstring>
using namespace std;
class mystring{
public:
//构造函数
mystring(const char *s=NULL)
{
if(!s){//没有传参数
this->len = 10;
this->str = new char[this->len];
memset(this->str,0,10);
}
else{
this->len = strlen(s)+1;
this->str = new char[this->len];
strcpy(this->str,s);
}
}
//拷贝构造函数
mystring(const mystring &s)
{
this->len = s.len;
this->str = new char[this->len];
strcpy(this->str,s.str);
}
//析构函数
~mystring()
{
delete[] this->str;
}
//打印字符串
void show()
{
cout<<this->str<<endl;
}
//获取空间大小
size_t get_len()
{
return this->len;
}
//获取字符串长度
size_t get_size()
{
return strlen(this->str);
}
//获取指定位置的字符
char at(size_t n)
{
if(n>=this->len){//越界
return -1;
}
return this->str[n];
}
//修改字符串内容 健壮性
void modify_str(const char *s)
{
if(!s){//为空修改为长度为10的空串
delete[] this->str;
this->len = 10;
this->str = new char[this->len];
memset(this->str,0,10);
}
else{//非空
if(this->len<strlen(s)+1){//空间不够
//调整空间
delete[] this->str;
this->len = strlen(s)+1;
this->str = new char[this->len];
}
strcpy(this->str,s);
}
}
private:
char *str;//字符串内容首地址
size_t len;//空间大小
};
int main()
{
//mystring str1;
mystring str1("welcome to GEC!");
str1.show();
mystring str2 = str1;
str2.show();
str1.modify_str("byebye");
str1.show();
str2.show();
cout<<str1.get_size()<<endl;
return 0;
}
类中的特殊成员
const对象和const成员
语法
class A{
public:
A(int n=0,int m=0):num(n),num_1(m){}
void show(){....}
void show()const{....}//const成员函数
private:
const int num;//const成员变量
int num_1;
};
A a;
const A b;//const对象
应用
1.const成员函数和同名的非const成员函数构成重载关系
非const对象优先调用非const函数,const对象只能调用const函数
2.const成员函数只能读取成员变量,不能修改成员变量
如果一定要修改,必须在声明该成员变量前加mutable修饰符
如果一个成员函数不会修改成员变量,就应该将其设计为const成员函数
3.const成员变量和const对象都不能修改
静态(static)成员
静态成员分为静态成员变量和静态成员函数,静态成员属于类,不属于某个对象。
静态成员变量
静态成员变量在成员变量声明前加static,必须初始化,而且应该在类外初始化,默认初始化为0,类类型的静态成员变量调用默认构造函数。
静态成员函数
静态成员变量在成员函数声明前加static,静态成员函数只能访问静态成员变量,不能访问普通成员变量。
语法:
class A{
public:
static int num;//静态成员变量
static void show(){......}//静态成员函数
};
//类外初始化
int A::num = xxx;
//访问静态成员
类名::静态成员;
静态成员无需通过对象来访问,可以直接通过类名访问。实现的机制是静态成员存储在独立的内存中,不能在在静态成员函数中使用this指针。所有同类型的对象访问的是同一个静态成员。
练习:
为mystring类提供一个静态成员函数,该函数返回一个指定长度的存储字符串的空间(char *)。
/*07-const对象和const成员*/
#include <iostream>
#include <cstring>
using namespace std;
class mystring {
public:
static char *getSpace(size_t t) {
return new char[t];
}
private:
size_t len;
char *s;
};
int main() {
char *string1 = mystring::getSpace(10);
strcpy(string1, "helloworld");
cout << string1 << endl;
cout << sizeof(string1) << endl; //8
return 0;
}
友元
友元的作用就是让类外数据突破访问权限的限制,可以将 类/函数 声明为某个类的友元,友元类和友元函数可以访问类中的所有数据,不受访问权限的限制。
友元函数
友元函数是一个全局函数,在类内将函数声明为友元,这个全局函数就可以访问类中的所有数据,语法如下:
在类内部声明:
friend 函数声明;
友元类
有一个类A,在类A中将类B声明为友元,类B就可以访问类A中的所有数据,语法如下:
在类的内部声明:
friend class 友元类名;
注意:友元不受访问权限的限制,可以访问类中的所有数据,破坏了类的封装属性,如非必要,不要使用。
作业:
实现以下类的构造函数(默认构造长度为10,值为0的数组),析构函数和拷贝构造函数
class MyArray{
public:
private:
int *pdata;//数组首地址
size_t len;//数组元素个数
};