默认成员函数
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
类的空类
我们在写类的成员变量和成员函数,我们实例化一个对象,有时候就会忘记实例化,直接使用成员函数,这个在C语言会报错,在c++就不会报错,但是值是随机的
#include<iostream> using std::cout; using std::endl; using std::cin; class Stru { public: void Fun() { cout << _a << endl; cout << _b << endl; } private: int* _a; int _b; }; int main() { Stru str; str.Fun(); return 0; }
有啥方法可以解决呢?答案是构造函数
构造函数
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证
每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。
构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任
务并不是开空间创建对象,而是初始化对象。
其特征如下:
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。
- 构造函数可以重载。
#include<iostream> using std::cout; using std::endl; using std::cin; class Stru { public: Stru() { _a = 200; _b = 100; } Stru(int a, int b) { _a = a; _b = b; } void Fun() { cout << _a << endl; cout << _b << endl; } private: int _a; int _b; }; int main() { Stru str; str.Fun(); Stru st1(1, 1); st1.Fun(); return 0; }
这里有两种写法,一种是直接使用自己定义的,一种是使用传入的参数,需要注意的是 第一种写法不用加(),因为加了就相当于是函数声明了。
注意:可以使用函数重载,但是要注意是否有冲突,缺省参数和无参数是否存在歧义。
如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
c++编译器默认构造函数:一般情况内置类型不会进行初始化,自定义类型的初始化会自动调用默认构造函数。
内置类型和自定义类的的自动初始化情况
#include<iostream> using std::cout; using std::endl; using std::cin; class Stack { public: Stack() { _a = (int *)malloc(sizeof(int) * 4); if (_a == nullptr) { perror("malloc"); return; } _top = 0;//栈顶元素的下一个 _capacity = 4; } //插入 void Stackpushback(int elemest) { if (_top == _capacity) { int size = _capacity > 0 ? 2 * _capacity : 4; int* tmp = (int*)realloc(_a, sizeof(int) * size); if (tmp == nullptr) { perror("realloc"); return; } _a = tmp; _capacity = size; } _a[_top++] = elemest; } //删除 void Stackpop() { if(_top) _top--; } //栈顶元素 int Stacktop() { if(_top) return _a[_top - 1]; return INT_MAX; } //是否为空 bool Stackempty() { return _top; } private: int* _a; int _top; int _capacity; }; class StackQueue { private: //入数据的栈 Stack pushstack; //出数据的栈 Stack popstack; int size; }; int main() { Stack mystack; StackQueue mq; return 0; }
可以看到size没有初始化就赋值了0,因为在vs2019优化过了,我们一般就认为是没有初始化化的,
内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认
注意一下:指针类型都是内置类型
但是在C++11就增加了补丁
**内置类型成员变量在类中声明时可以给默认值。**本质上还是声明,定义是要给空间的
总结:
- 我们不写构造函数,编译器会自己写一个构造函数,这个值是随机值,这个函数叫做默认构造
- 无参数构造函数可以叫默认构造
- 全缺省函数也叫默认构造
- 这三种不能同时存在,只能存在一个
析构函数
前面我们学习了构造函数,主要用于初始化对象
这里我们学习析构函数
析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由
编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
析构函数是特殊的成员函数,其特征如下:
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值类型。
- 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构
函数不能重载 - 对象生命周期结束时,C++编译系统系统自动调用析构函数。
#include<iostream> using std::cout; using std::endl; using std::cin; class Date { public: ~Date() { cout << "调用析构函数" << endl; } Date() { _a = 20; } private: int _a = 30; }; int main() { Date time; return 0; }
这个析构函数我们可以运用于经常忘记的内存释放的地方,比如栈使用结束后,空间释放
注意:析构调用完才销毁对象
拷贝函数
#include<iostream> using std::cout; using std::endl; using std::cin; class Date { public: Date(int year, int month, int day) { _year = year; _month = month; _day = day; } void print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; void Fun(Date d) { d.print(); } int main() { Date d1(2024, 1, 4); Fun(d1); return 0; }
这种是一种浅拷贝,但是当浅拷贝运用到某些场景就会带来不必要的麻烦
当Fun函数运行的时候是有可能更改对应地址的值的
拷贝函数的特征
拷贝构造函数也是特殊的成员函数,其特征如下:
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,
因为会引发无穷递归调用。
#include<iostream> using std::cout; using std::endl; using std::cin; class Date { public: Date(int year, int month, int day) { _year = year; _month = month; _day = day; } ~Date() { cout << "调用析构函数" << endl; } Date(const Date& dd) { _year = dd._year; _month = dd._month; _day = dd._day; } void print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2024, 1, 3); Date d2(d1); return 0; }
3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
#include<iostream> using std::cout; using std::endl; using std::cin; class Date { public: Date(int year, int month, int day) { _year = year; _month = month; _day = day; } ~Date() { cout << "调用析构函数" << endl; } Date(const Date& dd) { _year = dd._year; _month = dd._month; _day = dd._day; } void print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; void Fun(Date d) { d.print(); } int main() { Date d1(2024, 1, 3); Date d2(d1); Fun(d1); return 0; }
这里的dd是是d1的引用,因为形参是实参的临时拷贝,这里的this指针指向的是d,
这个图也可以解释出,为啥拷贝函数传值会陷入无限的循环
c++初阶------类和对象(六大默认构造函数的揭破)-2