初夏小谈:类和对象(三)之默认成员函数
上一篇说明类中六大默认函数之构造函数,今天来说说剩下的5个默认函数。
一、析构函数 1. 什么是析构函数?
从构造函数我们知道,它是在创建对象时为对象赋一个合适的初始值,就是初始化对象。而构造函数则和构造函数相反。它是在对象生命周期结束时,编译器自动调用析构函数来清理这个对象的资源的工作。
2.析构函数的特性:
①:无参数无返回值
②:函数名就是类名的前面加~
③:如果我们自定义析构函数,则编译器不在生成。否咋编译器便会自动生成默认的析构函数
④:调用时机:
析构函数是在对象生命周期结束时编译器进行自动调用。
⑤:作用:清空对象的资源,防止资源泄露
二、拷贝构造函数
1.什么是拷贝构造函数?
拷贝构造函数就是构造函数不同形式,只是它里面只有一个形参,并且这个形参是对本类类型对象的引用(由于是对一个对象的引用,只是用来拷贝一个对象所以不需要改变被拷贝的对象则加上const)。在用已有的一个类类型对象去创建新对象时由编译器自动调用。
2.拷贝构造函数注意一下几点:
①:拷贝构造函数是构造函数的一个重载形式。
②:我们没有自定义拷贝构造函数时。编译器会自动生成默认拷贝构造函数。
③:当被引用对象存在时,编译器就会自动调用来用已经存在的对象来创建新对象。拷贝构造函数的参数只有一个。源对象和新对象是同一个类型。
④:在拷贝时要引用而不传值呢?
因为传值会发生无穷递归。因为再传值时,该形参是一个类类型对象,想要将实参拷贝到形参1中,就会再次调用拷贝构造函数来给形参2赋值。目的是给形参1赋值。而形参2又是一个类类型对象,将实参拷贝到实参2中时,就会调用拷贝构造函数,来给形参3赋值,来达到给形参2赋值的目的。而形参3又是一个类类型对象......就这样无穷递归下去
而传引用时就无需再进行值的拷贝。因为形参就是实参,不会调用拷贝构造函数来创建形参。
⑤:默认拷贝构造会自动把所拷贝对象拷贝到新对象中,那么怎么有时也要自定义拷贝构造函数?
因为默认的拷贝构造函数是进行内存拷贝(浅拷贝)值拷贝。如果当一个类中要拷贝指针,那么创建的新对象就把源对象指针里面存的地址拷贝一份放进新的对象中。二者指向同一块内存空间,在调用析构函数进行清理时先清理新对象(函数调用源对象先压栈后被释放),指针所指向空间被释放,此时源对象里面的指针变为野指针,再去释放源对象的指针就会崩溃。
例:
//不能使用编译器自动调用拷贝构造函数例子 //测试用例: class String { public: String(const char* ptr = "SUST"); ~String(); private: char* _ptr; };
//不能使用编译器自动调用拷贝构造函数例子 //测试用例: String::String(const char* ptr) { if (ptr == nullptr) ptr = ""; _ptr = (char*)malloc(sizeof(ptr) + 1); cout << "构造函数" << this << endl; } String::~String() { cout << "析构函数" << this << endl; free(_ptr); _ptr = nullptr; }
void TestStr() { String Str; String Str1(Str); } int main() { TestStr(); system("pause"); return 0; }
结果:
三、赋值运算符重载
1.运算符重载
①:概念:在内置类型时我们可以进行各种运算操作。那么在自定义类型时如何进行对象的运算操作呢?
C++引入了运算符重载函数,它是具有特殊函数名的函数,其它和设置除了const和普通函数没什么区别,它的函数名是关键字operator后面紧跟运算符。
②:函数原型:返回值类型 operator运算符(参数)
2.运算符重载特性:
①:不能用其它符号来创建新的操作符operator@
②:重载操作符时必须有一个自定义类型。
③:内置类型的操作符含义不能变
④:在类成员的重载函数时,我们看着是一个参数。实际再传参时会将this指针传进去 ,并且是第一个形参。
⑤:.*、sizeof、::、?:、.这五个运算符不能重载
(2)赋值运算符重载
1.赋值运算符六点:①:有返回值 是为了能够连续赋值
②:参数类型 const 类型& const是这个参数不能被修改,使用引用是为了程序的高效性。当参数是内置类型时则不用引用。
③:需要检测自己给自己赋值 为了防止多余的调用
④:返回*this指针。因为它的生命周期在函数中是最长的
⑤:如果我们没有自定义赋值运算符重载,编译器会自动生成,但是生成的会按值拷贝
⑥:6.哪些类的赋值运算符重载一定需要用户显式提供?只要类中涉及到资源的管理, 就需要用户显示提供。
(3)const修饰成员函数(将const加在成员函数的后面!!!)
①实际上修饰的是隐藏的this指针。this指针原本不能改变所指向的位置即:类型:Date* const 现在加上const则不能修改this指针所指向的内容即:this指针所指向的成员变量不能被修改。
类型为:const Date* const
②有时我们希望修改其中的一两个而其它的不能被修改时该怎么办?
但是并不是绝对不能被修改。如果仍想在const修饰的成员函数里面修改成员变量。则在想修改的成员变量的定义位置前加上mutable。然后就可以在const所修饰的成员函数里面修改mutable所修饰的成员变量。
mutable:该成员变量在const成员函数中可以修改。
1.const对象可以调用非const成员函数吗?
不可以,如果能调用的话,在非const成员函数中有可能会修改成员变量,与const修饰的一样冲突。如果没有mutable修饰的成员变量的话。
2.非const对象可以调用const成员函数吗?
可以。普通类型的对象不仅可以调用普通得成员函数,也可以调用const成员函数。因为普通类型是既可以被修改,也可以不修改。
3.const成员函数内可以调用其它非const成员函数吗?
不可以。因为被const修饰的成员函数,则this指针被锁定不能修改所指向的内容(成员变量)。而再调普通成员函数this被传进来,而它意思是可以修改this指针所指对象的成员变量。这与const成员函数意愿相违背。
4.非const成员函数内可以调用其它const成员函数吗?
可以。非const成员函数对this指针所指对象的成员变量的权利是既可以修改也可以不改。当再次调const成员函数时它说this所指对象的成员变量不可修改。这符合非const函数的要求。
5.当对象是被const修饰时,它调用被const修饰的成员函数则返回值不能是普通类型,因为普通类型可以被修改。所以返回值前加const限制不能被修改。
四、取地址及const取地址操作符重载
一般不需要我们自定义,编译器自动生成的默认取地址的重载即可,特殊情况想要获取指定内容就需要自定义。
珍&源码