本文主要是介绍学习记录——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;
}
四、拷贝构造函数
拷贝构造函数,是一个特殊的构造函数
1、相关概念
1)没有返回值
2)函数名与类同名
3)参数:该类的其他对象的引用
4)访问权限:一般为public
5)定义格式:
类名 (类名 &other)
2、调用时机
1)使用一个类对象给另一个类对象初始化时
string s1("hello world"); //调用有参构造
string s2 = s1; //调用拷贝构造
string s3(s1); //调用拷贝构造
2)使用值传递时,实参取代形参的过程时,系统调用拷贝构造
3)使用值返回时,返回结果也是调用拷贝构造
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, double s):name(n),age(a),score(s) {cout<<"Stu::有参构造"<<endl;} //有参构造~Stu(){cout<<"Stu::析构函数"<<endl;} //析构函数//自定义拷贝构造函数Stu(Stu &other):name(other.name),age(other.age),score(other.score){cout<<"Stu::拷贝构造函数"<<endl;}//定义展示函数void show(){cout<<"name = "<<name<<endl;cout<<"age = "<<age<<endl;cout<<"score = "<<score<<endl;}};//定义函数传递对象
Stu fun(Stu s)
{s.show();cout<<"fun 函数结束"<<endl;return s;
}int main()
{Stu s1("zhangpp", 18, 99); //调用有参构造构造一个s1对象s1.show();cout<<"********************************************"<<endl;Stu s2 = s1; //调用拷贝构造s2.show();cout<<"********************************************"<<endl;Stu s3(s1); //调用拷贝构造s3.show();cout<<"********************************************"<<endl;//调用功能函数fun(s1);cout<<"********************************************"<<endl;return 0;
}
4、类中成员指针的释放问题(深浅拷贝问题)
定义一个类时,如果没有手动提供拷贝构造函数,系统会自动提供一个拷贝构造函数,来完成对象中成员的初始化工作,这个拷贝构造函数称为浅拷贝,如果类中没有指针成员,使用浅拷贝是没有问题的。
如果类中有指针成员,并且指向的其他空间时,使用浅拷贝就会出现多个对象中指针成员指向同一个内存空间,析构指针空间时,会造成对同一块内存空间的多次释放的情况,造成段错误(double free),此时就需要深拷贝。
深拷贝需要程序手动定义拷贝构造函数,在拷贝构造函数的初始化列表中,给新对象的指针成员重新分配空间,将原来对象指针空间指向的内容拷贝到该空间中即可完成深拷贝。
#include <iostream>using namespace std;class Stu
{
private:string name;int age;double score;int *ptr; //指针成员public:Stu() {cout<<"Stu::无参构造"<<endl;} //无参构造Stu(string n, int a, double s):name(n),age(a),score(s),ptr(new int(520)) {cout<<"Stu::有参构造"<<endl;} //有参构造~Stu(){delete ptr; //释放堆区空间cout<<"Stu::析构函数"<<endl;} //析构函数//自定义拷贝构造函数:深拷贝Stu(Stu &other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr)){cout<<"Stu::拷贝构造函数"<<endl;}//定义展示函数void show(){cout<<"name = "<<name<<endl;cout<<"age = "<<age<<endl;cout<<"score = "<<score<<endl;cout<<"*ptr = "<<*ptr<<endl;cout<<"ptr = "<<ptr<<endl;}};int main()
{Stu s1("zhangpp", 18, 99); //调用有参构造构造一个s1对象s1.show();cout<<"********************************************"<<endl;Stu s2 = s1; //调用拷贝构造s2.show();cout<<"********************************************"<<endl;return 0;
}
五、移动构造函数
如果类中不显性定义拷贝构造函数或者移动构造函数,那么系统会自动提供这两个函数,如果程序员手动定义了其中任意一个函数,系统就两个都不提供了,如果需要使用,则需要程序员自己手动定义
1、相关概念
1)移动构造函数,是将临时空间或将亡空间,托管给一个新的对象。
2)没有返回值
3)函数名与类同名
4)参数:该类的其他对象的右值引用
5)访问权限:一般为public
6)定义格式: 类名 (类名 &&other)
2、调用示例
#include <iostream>using namespace std;class Stu
{
private:string name;int age;double score;int *ptr; //指针成员public:Stu() {cout<<"Stu::无参构造"<<endl;} //无参构造Stu(string n, int a, double s):name(n),age(a),score(s),ptr(new int(520)) {cout<<"Stu::有参构造"<<endl;} //有参构造~Stu(){delete ptr; //释放堆区空间cout<<"Stu::析构函数"<<endl;} //析构函数//自定义拷贝构造函数:深拷贝Stu(Stu &other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr)){cout<<"Stu::拷贝构造函数"<<endl;}//自定义移动构造函数Stu(Stu &&other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr)){cout<<"Stu::移动构造函数"<<endl;}//定义展示函数void show(){cout<<"name = "<<name<<endl;cout<<"age = "<<age<<endl;cout<<"score = "<<score<<endl;cout<<"*ptr = "<<*ptr<<endl;cout<<"ptr = "<<ptr<<endl;}};int main()
{Stu s1("zhangpp", 18, 99); //调用有参构造构造一个s1对象s1.show();cout<<"********************************************"<<endl;Stu s2 = s1; //调用拷贝构造s2.show();cout<<"********************************************"<<endl;//调用移动构造函数Stu s3 = move(s1);s3.show();cout<<"********************************************"<<endl;return 0;
}
六、拷贝赋值函数
拷贝赋值函数也称等号运算符重载
1、相关概念
2、调用时机
当使用一个类对象给另一个类对象赋值时,会自动调用拷贝赋值函数
string s1("hello world"); //有参构造
string s2 = s1; //拷贝构造
string s3; //无参构造
s3 = s1; //拷贝赋值函数
3、拷贝复制函数,如果不手动定义,系统会默认提供一个拷贝复制函数,如果手动定义了拷贝复制函数,那么系统就不会提供了。如果手动定义了移动构造函数,拷贝赋值函数不可用
4、拷贝赋值函数也涉及深浅拷贝的问题:
定义一个类时,如果没有手动提供拷贝赋值函数,系统会自动提供一个拷贝赋值函数,来完成对象中成员的赋值工作,这个拷贝赋值函数称为浅拷贝,如果类中没有指针成员,使用浅拷贝是没有问题的。
如果类中有指针成员,并且指向的其他空间时,使用浅拷贝就会出现多个对象中指针成员指向同一个内存空间,析构指针空间时,会造成对同一块内存空间的多次释放的情况,造成段错误(double free),此时就需要深拷贝。
深拷贝需要程序手动定义拷贝赋值函数,在拷贝赋值函数的函数体内,给新对象的指针成员重新分配空间,将原来对象指针空间指向的内容拷贝到该空间中即可完成深拷贝。
#include <iostream>using namespace std;class Stu
{
private:string name;int age;double score;int *ptr; //指针成员public:Stu():ptr(new int(0)) {cout<<"Stu::无参构造"<<endl;} //无参构造Stu(string n, int a, double s):name(n),age(a),score(s),ptr(new int(520)) {cout<<"Stu::有参构造"<<endl;} //有参构造~Stu(){delete ptr; //释放堆区空间cout<<"Stu::析构函数"<<endl;} //析构函数//自定义拷贝构造函数:深拷贝Stu(const Stu &other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr)){cout<<"Stu::拷贝构造函数"<<endl;}//自定义移动构造函数Stu(Stu &&other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr)){cout<<"Stu::移动构造函数"<<endl;}//自定义拷贝赋值函数Stu & operator=(const Stu &other){if(this != &other) //防止自己给自己赋值{//完成成员的赋值工作this->name = other.name;this->age = other.age;this->score = other.score;// delete this->ptr; //释放自己之前的空间// this->ptr = new int(*other.ptr); //深拷贝*this->ptr = *other.ptr; //深拷贝}cout<<"拷贝赋值函数"<<endl;return *this; //返回自身的引用}//定义展示函数void show(){cout<<"name = "<<name<<endl;cout<<"age = "<<age<<endl;cout<<"score = "<<score<<endl;cout<<"*ptr = "<<*ptr<<endl;cout<<"ptr = "<<ptr<<endl;}};int main()
{Stu s1("zhangpp", 18, 99); //调用有参构造构造一个s1对象s1.show();cout<<"********************************************"<<endl;Stu s2 = s1; //调用拷贝构造s2.show();cout<<"********************************************"<<endl;//调用移动构造函数Stu s3 = move(s1);s3.show();cout<<"********************************************"<<endl;//调用拷贝赋值函数Stu s4; //无参构造s4 = s1; //拷贝赋值函数 s4.operator=(s1)s4.show();cout<<"********************************************"<<endl;return 0;
}
七、移动赋值函数
如果没有手动定义移动赋值函数,也没有手动定义任意一个特殊成员函数,此时,系统会自动提供一个移动赋值函数。如果提供了任意一个特殊成员函数,移动赋值函数就不提供了
#include <iostream>using namespace std;class Stu
{
private:string name;int age;double score;int *ptr; //指针成员public:Stu():ptr(new int(0)) {cout<<"Stu::无参构造"<<endl;} //无参构造Stu(string n, int a, double s):name(n),age(a),score(s),ptr(new int(520)) {cout<<"Stu::有参构造"<<endl;} //有参构造~Stu(){delete ptr; //释放堆区空间cout<<"Stu::析构函数"<<endl;} //析构函数//自定义拷贝构造函数:深拷贝Stu(const Stu &other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr)){cout<<"Stu::拷贝构造函数"<<endl;}//自定义移动构造函数Stu(Stu &&other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr)){cout<<"Stu::移动构造函数"<<endl;}//自定义拷贝赋值函数Stu & operator=(const Stu &other){if(this != &other) //防止自己给自己赋值{//完成成员的赋值工作this->name = other.name;this->age = other.age;this->score = other.score;// delete this->ptr; //释放自己之前的空间// this->ptr = new int(*other.ptr); //深拷贝*this->ptr = *other.ptr; //深拷贝}cout<<"拷贝赋值函数"<<endl;return *this; //返回自身的引用}//定义移动赋值函数Stu & operator=(Stu &&other){if(this != &other) //防止自己给自己赋值{//完成成员的赋值工作this->name = other.name;this->age = other.age;this->score = other.score;// delete this->ptr; //释放自己之前的空间// this->ptr = new int(*other.ptr); //深拷贝*this->ptr = *other.ptr; //深拷贝}cout<<"移动赋值函数"<<endl;return *this; //返回自身的引用}//定义展示函数void show(){cout<<"name = "<<name<<endl;cout<<"age = "<<age<<endl;cout<<"score = "<<score<<endl;cout<<"*ptr = "<<*ptr<<endl;cout<<"ptr = "<<ptr<<endl;}};class name
{
public:name() {}
};int main()
{Stu s1("zhangpp", 18, 99); //调用有参构造构造一个s1对象s1.show();cout<<"********************************************"<<endl;Stu s2 = s1; //调用拷贝构造s2.show();cout<<"********************************************"<<endl;//调用移动构造函数Stu s3 = move(s1);s3.show();cout<<"********************************************"<<endl;//调用拷贝赋值函数Stu s4; //无参构造s4 = s1; //拷贝赋值函数 s4.operator=(s1)s4.show();cout<<"********************************************"<<endl;Stu s5; //无参构造s5 = move(s1); //移动赋值函数s5.show();cout<<"********************************************"<<endl;return 0;
}
七、取地址运算符重载
功能:取得当前类对象的地址
1、相关概念
1)返回值:当前类的指针
2)参数:无
3)权限:一般为public
4)格式: 类名 * operator&()
2、 如果没有手动定义取地址运算符重载函数,系统会自动提供一个取地址运算符重载函数,如果手动定义了,系统就不再提供了
#include <iostream>using namespace std;class Stu
{
private:string name;int age;double score;int *ptr; //指针成员public:Stu():ptr(new int(0)) {cout<<"Stu::无参构造"<<endl;} //无参构造Stu(string n, int a, double s):name(n),age(a),score(s),ptr(new int(520)) {cout<<"Stu::有参构造"<<endl;} //有参构造~Stu(){delete ptr; //释放堆区空间cout<<"Stu::析构函数"<<endl;} //析构函数//自定义拷贝构造函数:深拷贝Stu(const Stu &other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr)){cout<<"Stu::拷贝构造函数"<<endl;}//自定义移动构造函数Stu(Stu &&other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr)){cout<<"Stu::移动构造函数"<<endl;}//自定义拷贝赋值函数Stu & operator=(const Stu &other){if(this != &other) //防止自己给自己赋值{//完成成员的赋值工作this->name = other.name;this->age = other.age;this->score = other.score;// delete this->ptr; //释放自己之前的空间// this->ptr = new int(*other.ptr); //深拷贝*this->ptr = *other.ptr; //深拷贝}cout<<"拷贝赋值函数"<<endl;return *this; //返回自身的引用}//定义移动赋值函数Stu & operator=(Stu &&other){if(this != &other) //防止自己给自己赋值{//完成成员的赋值工作this->name = other.name;this->age = other.age;this->score = other.score;// delete this->ptr; //释放自己之前的空间// this->ptr = new int(*other.ptr); //深拷贝*this->ptr = *other.ptr; //深拷贝}cout<<"移动赋值函数"<<endl;return *this; //返回自身的引用}//自定义取地址运算符重载函数Stu * operator&(){cout<<"取地址运算符重载函数"<<endl;return this;}//定义展示函数void show(){cout<<"name = "<<name<<endl;cout<<"age = "<<age<<endl;cout<<"score = "<<score<<endl;cout<<"*ptr = "<<*ptr<<endl;cout<<"ptr = "<<ptr<<endl;}};class name
{
public:name() {}
};int main()
{Stu s1("zhangpp", 18, 99); //调用有参构造构造一个s1对象s1.show();cout<<"********************************************"<<endl;Stu s2 = s1; //调用拷贝构造s2.show();cout<<"********************************************"<<endl;//调用移动构造函数Stu s3 = move(s1);s3.show();cout<<"********************************************"<<endl;//调用拷贝赋值函数Stu s4; //无参构造s4 = s1; //拷贝赋值函数 s4.operator=(s1)s4.show();cout<<"********************************************"<<endl;//调用移动赋值函数Stu s5; //无参构造s5 = move(s1); //移动赋值函数s5.show();cout<<"********************************************"<<endl;//取地址运算符重载函数&s1;cout<<"********************************************"<<endl;return 0;
}
模仿string 封装一个类
#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();//lengthint 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;
}
这篇关于学习记录——day40 类中特殊的成员函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!