学习记录——day40- 类中特殊的成员函数
一、类中提供的特殊成员函数
构造函数、析构函数、拷贝构造函数、拷贝复制函数、移动构造函数、移动复值函数、取地址运算符重载
如果用户不显性定义这些函数,系统也会提供指向函数,如果用户显性定义了这些函数,系统就不会提供了。
这些函数无论是系统提供还是用户定义,都不需要手动调用,特殊时机,系统自动调用
二、构造函数
使用类去实例化对象时,为对象进行资源的申请以及初始化使用的
1、构造函数相关概念
1、构造函数没有返回值(不是 void ,单纯没有)
2、函数名与类同名
3、访问权限,一般为public
4、类名(形参列表){函数体内容}
2、调用时机:当使用类实例化对象时,系统自动调用
1、栈区空间:使用类实例化对象时,系统自动调用
类名 变量名(实参); //此时就调用的构造函数
2、堆区空间:只有在使用new申请空间时,自动调用构造函数
类名 *指针名; //此时仅仅只是定义一个指针变量,并没有为对象分配空间
指针名 = new 类名(实参); //此时调用的构造函数
#include <iostream>
using namespace std;
class Stu
{
public:
Stu()
{
cout<<"Stu的构造函数"<<endl;
}
};
int main()
{
//在栈区申请对象空间
Stu s1;
//堆区空间申请对象
Stu *ptr; //此时不调用构造函数
ptr = new Stu; //此时调用构造函数
return 0;
}
3、构造函数定义相关
1)构造函数分为有参构造和无参构造函数,有参构造函数可以为成员属性进行初始化,这些构造函数之间构成重载关系
2)一个类中可以有多个构造函数,每个对象仅仅只会调用一个构造函数
3)如果类中没有定义构造函数,那么,系统会自动提供一个无参构造函数,用于对对象空间的申请,如果程序员自己定义了构造函数,系统就不再提供那个无参构造了,如果还想使用无参构造,需要自己定义一个无参构造
#include <iostream>
using namespace std;
class Stu
{
private:
string name;
int age;
double score;
public:
//无参构造
Stu()
{
cout<<"Stu的无参构造函数"<<endl;
}
//自定义有参构造
Stu(string n, int a)
{
this->name = n;
this->age = a;
cout<<"Stu::有参构造"<<endl;
}
void show()
{
cout<<"name = "<<name<<endl;
cout<<"age = "<<age<<endl;
cout<<"score = "<<score<<endl;
}
};
int main()
{
//在栈区申请对象空间
Stu s1; //此时调用了无参构造,只为对象申请了空间,但是没有为对象初始化
s1.show();
cout<<"**************************************"<<endl;
//申请一个对象,调用有参构造
Stu s2("zhangsan", 18); //此时调用了有参构造
s2.show();
cout<<"**************************************"<<endl;
//堆区空间申请对象
Stu *ptr; //此时不调用构造函数
ptr = new Stu; //此时调用构造函数
return 0;
}
4、构造函数初始化
使用方式:
类名(形参1,形参2,。。。形参n):成员1(形参1),成员2(形参2)......成员n(形参n)
{函数体内容}
说明:在构造函数的小括号后,由冒号引出初始化列表,括号外时成员变量,括号内是形参
#include <iostream>
using namespace std;
class Stu
{
private:
string name;
int age;
double score;
public:
//无参构造
Stu()
{
cout<<"Stu的无参构造函数"<<endl;
}
//自定义有参构造
Stu(string n, int a)
{
this->name = n; //对成员的赋值操作
this->age = a;
cout<<"Stu::有参构造1"<<endl;
}
//定义有参构造:使用初始化列表完成对成员的初始化工作
Stu(string n, int a, double s):name(n),age(a),score(s)
{
cout<<"Stu::有参构造2"<<endl;
}
void show()
{
cout<<"name = "<<name<<endl;
cout<<"age = "<<age<<endl;
cout<<"score = "<<score<<endl;
}
};
int main()
{
//在栈区申请对象空间
Stu s1; //此时调用了无参构造,只为对象申请了空间,但是没有为对象初始化
s1.show();
cout<<"**************************************"<<endl;
//申请一个对象,调用有参构造
Stu s2("zhangsan", 18); //此时调用了有参构造
s2.show();
cout<<"**************************************"<<endl;
//申请一个对象,调用有参构造
Stu s3("lisi", 20, 99.8);
s3.show();
cout<<"**************************************"<<endl;
//堆区空间申请对象
Stu *ptr; //此时不调用构造函数
ptr = new Stu; //此时调用构造函数
return 0;
}
5、必须使用初始化列表的情况
1)当构造函数的形参名和成员变量名同名时,可以使用初始化列表来解决
2)当类中有const修饰的成员变量时,对该变量也必须进行初始化,使用初始化列表解决
3)当类中有引用成员时,对该成员的操作也必须使用初始化列表完成
4)当类中有其他类的成员子对象时,对该成员的操作也必须使用初始化列表完成,如果没有使用初始化列表调用有参构造,则系统会自动调用成员子对象的无参构造
#include <iostream>
using namespace std;
class Toy
{
public:
Toy() {cout<<"Toy::无参构造"<<endl;}
Toy(string n):name(n) {cout<<"Toy::有参构造"<<endl;}
private:
string name;
};
class Stu
{
private:
string name;
int age;
double score;
const int value; //类中有const类型的成员
int &key; //类中有引用成员
Toy t; //类中有其他类的成员子对象
public:
//无参构造
Stu():value(1314), key(*(new int(520))), t("hello kity")
{
cout<<"Stu的无参构造函数"<<endl;
}
//自定义有参构造
Stu(string n, int a, int &k):value(1314), key(k)
{
this->name = n; //对成员的赋值操作
this->age = a;
cout<<"Stu::有参构造1"<<endl;
}
//定义有参构造:使用初始化列表完成对成员的初始化工作
Stu(string name, int age, double score, int &k):name(name),age(age),score(score),value(1314),key(k)
{
cout<<"Stu::有参构造2"<<endl;
}
void show()
{
cout<<"name = "<<name<<endl;
cout<<"age = "<<age<<endl;
cout<<"score = "<<score<<endl;
}
};
int main()
{
//在栈区申请对象空间
Stu s1; //此时调用了无参构造,只为对象申请了空间,但是没有为对象初始化
s1.show();
cout<<"**************************************"<<endl;
//堆区空间申请对象
Stu *ptr; //此时不调用构造函数
ptr = new Stu; //此时调用构造函数
return 0;
}
三、析构函数
功能:在对象消亡时,用于给对象回收空间使用的
1、析构函数相关概念
1)没有返回值(不是 void ,就是没有)
2)函数名:类名前加个波浪线 ~类名
3)权限:一般为public
4)没有参数,所以,一个类中只有一个析构函数,不能进行重载
5)格式: ~类名()
2、调用时机:当对象的生命周期结束后,用于回收内存空间
栈区:当栈空间释放后,系统会自动调用该类的析构函数
堆区:当使用delete关键字释放对象空间时,系统自动调用
3、如果没有手动定义析构函数,系统会提供一个析构函数,用于回收类对象的空间,如果手动定义了析构函数,那么,系统就不再提供默认的析构函数了
4、析构函数示例
#include <iostream>
using namespace std;
class Toy
{
public:
Toy() {cout<<"Toy::无参构造"<<endl;}
Toy(string n):name(n) {cout<<"Toy::有参构造"<<endl;}
private:
string name;
};
class Stu
{
private:
string name;
int age;
double score;
const int value; //类中有const类型的成员
int &key; //类中有引用成员
Toy t; //类中有其他类的成员子对象
int *ptr; //指针成员
public:
//无参构造
Stu():value(1314), key(*(new int(520))), t("hello kity"), ptr(new int(666))
{
cout<<"Stu的无参构造函数"<<endl;
}
//自定义有参构造
Stu(string n, int a, int &k):value(1314), key(k)
{
this->name = n; //对成员的赋值操作
this->age = a;
cout<<"Stu::有参构造1"<<endl;
}
//定义有参构造:使用初始化列表完成对成员的初始化工作
Stu(string name, int age, double score, int &k):name(name),age(age),score(score),value(1314),key(k)
{
cout<<"Stu::有参构造2"<<endl;
}
void show()
{
cout<<"name = "<<name<<endl;
cout<<"age = "<<age<<endl;
cout<<"score = "<<score<<endl;
}
//定义析构函数
~Stu()
{
delete ptr; //释放指针的空间
cout<<"STU::析构函数"<<endl;
}
};
int main()
{
//在栈区申请对象空间
Stu s1; //此时调用了无参构造,只为对象申请了空间,但是没有为对象初始化
s1.show();
cout<<"**************************************"<<endl;
//堆区空间申请对象
Stu *ptr; //此时不调用构造函数
ptr = new Stu; //此时调用构造函数
//释放ptr的空间
delete ptr; //此时会调用析构函数
return 0;
}
作业
#include <iostream>
#include<cstring>
using namespace std;
class myString
{
private:
char *str; //记录c风格的字符串
int size; //记录字符串的实际长度
public:
//无参构造
myString():size(10)
{
str = new char[size]; //构造出一个长度为10的字符串
}
//有参构造
myString(const char *s); //有参构造 string s("hello wirld");
//判空函数
bool empty();
//size函数
int my_size();
//length
int my_length();
//c_str函数
char* my_str();
//at函数
char& at(int index);
//二倍扩容
bool expend();
};
int main()
{
cout << "Hello World!" << endl;
return 0;
}
myString::myString(const char *s)
{
size = strlen (s);
str = new char[size + 1];
strcpy(str,s);
}
bool myString::empty()
{
return !size;
}
int myString::my_size()
{
return size;
}
int myString::my_length()
{
return strlen(str);
}
char* myString::my_str()
{
return str;
}
char& myString::at(int index)
{
return str[index];
}
bool myString::expend()
{
size *= 2;
char* temp;
temp = new char[size +1];
delete str;
str = temp;
temp = NULL;
return true;
}