类和对象(中)

类和对象(中)

一、类的默认成员函数

类的默认成员函数是在我们不显示实现的前提下,编译器隐式实现的成员函数,总共有六个,示意图如下

常用的默认成员函数就是这六个

1、初始化、销毁:构造函数、析构函数

2、拷贝复制:拷贝构造、复制重载

3、取地址重载:编译器自带的默认成员函数基本是够用的,不细讲

二、构造函数

作用:构造函数并不是为成员变量开空间,而是在成员变量实例化的时候初始化成员变量
构造函数的三种形式

1、默认构造函数:系统自己生成的构造函数

2、显式定义构造函数:我们写的无参的、全缺省的构造函数(注:这两个函数不可以同时存在)

3、拷贝构造函数:使用一个已有的类的实例来构造一个新的实例

2.1 默认构造函数

cpp 复制代码
#pragma once
#include<iostream>
using namespace std;

typedef int DataType ;
class Stack
{
	DataType* arr;
	int _size;
	int _capacity;
};
};

这是未显式实现构造函数的日期类,我们来看看这个类中的成员变量被初始化成了什么样子

这里的int类型成员变量是被初始化成了随机值(也就是垃圾值),而int*类型的成员变量则是初始化成了空指针,这显然不是我们想要的结果,这时候我们就应该想办法实现自己的构造函数了

2.2 显式构造函数的实现

1、构造函数的名字就是类的名字,返回值是void并且不显式写出

2、构造函数可以构成函数重载

3、构造函数可以缺省

我们来实现一下

cpp 复制代码
Stack(int capacity = 4)
{
	_capacity = capacity;
	_stack = (int*)malloc(sizeof(DataType)* _capacity);
	_size = 0;
}

我们来看看缺省调用的结果

可以看到:_capacity和_size都被初始化了,_stack也分配了空间,这是符合我们的预期的

注意

如果一个类中包含了其他的自定义类型,而这个自定义类型也正好有构造函数,这个类没有写构造函数,就会自动调取其他自定义类型的构造函数,如果没有,就会报错

三、析构函数

析构函树主要是用来释放申请的空间(即malloc、calloc的空间),其他的内置类型成员函数是不归他管的,这些在栈区的成员函数会在生命周期结束后 自动销毁所以像是Date类这样的类型实际上是不需要析构的

3.1析构函数的注意事项

  • 析构函数的函数名是~+类名,返回值是void且不写、没有参数(和构造函数是挺像的)
  • 析构函数在类的生命周期结束时自动调用
  • 跟构造函数类似,我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理,⾃定类型成员会调⽤他的析构函数。

析构函数的实现

cpp 复制代码
~Stack()
{
	free(_stack);
}

3.2析构函数的调用顺序

在有多个类需要析构的时候,析构函数遵循先构造的先析构的原则

四、拷贝构造函数

如果⼀个构造函数的第⼀个参数是⾃⾝类类型的引⽤,且任何额外的参数都有默认值,则此构造函数也叫做拷⻉构造函数,也就是说拷⻉构造是⼀个特殊的构造函数。

4.1 拷贝函数的简单演示

这里我使用Date日期类来展现

cpp 复制代码
class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	~Date()
	{

	}
	Date(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
};

我们在主函数中试一试效果

cpp 复制代码
void test5()
{
	Date d1(2026, 1, 30);
	Date d2 = d1;
	std::cout << d1 << std::endl<< d2 << std::endl;
	//这里的输入流重载后面会讲到
}

4.2 拷贝构造函数的意义

系统的默认拷贝构造函数是在有限场景下适用的

在你没有自己写拷贝构造函数的时候,系统默认是将传入的类的数据逐个字节的拷贝(亦可称之为浅拷贝)

这种拷贝方式在诸如日期的情景下是完全够用的,但是在拷贝在堆区申请了内存的数据时就显得很鸡肋,这时候我们浅拷贝的数据实际上是一个地址

这会使得两个类指向的空间是同一个,然后在程序的末尾,先后调用析构函数,同一块空间被释放两次,函数报错(就成功的寄掉了)

这时候我们就需要自己写一个拷贝函数,从而将数据完美无误的迁移过去

这里可以参考我之前讲的一道力扣题链表的终极考验:随机链表的复制辅助理解

4.3 拷贝构造函数的注意事项

1、函数第一个参数一定是类类型对象的引用

类类型的传值引用是要调用一次拷贝构造、创造一个临时变量

如果我们写的拷贝构造函数中第一个参数类型是类类型的值,在调用这个类类型的时候就会调用这个拷贝构造函数,这时候又会回到原来的困境,然后一直调用、一直没法彻底的结束这个进程,然后就死循环了。

2、传值返回

传值返回是创建了一个临时的变量,这时候会调用一次拷贝构造函数,但是要注意的是这个临时变量是不可修改的且生命周期是短暂的,的想要延长他的生命周期就需要使用const左值引用

cpp 复制代码
lass MyClass {
public:
    int value;
    MyClass(int v) : value(v) {}
    void modify() { value = 100; }
};

MyClass createObject() {
    return MyClass(42);  // 返回临时对象
}

int main() {
    const MyClass& ref = createObject();  //  临时对象生命周期延长
    // ref.value = 20;  //  通过const引用也不能修改

3、传引用返回

传引用返回相当于是返回了原变量的地址,这种返回方式开销较小。

但是如果我们的变量在出了它的作用域之后就销毁了,这时候引用返回指向了一块被释放的空间,这是有问题的,这时候就出现了野引用(像是野指针

相关推荐
飞鹰512 小时前
深度学习算子CUDA优化实战:从GEMM到Transformer—Week4学习总结
c++·人工智能·深度学习·学习·transformer
2301_765703142 小时前
C++中的职责链模式实战
开发语言·c++·算法
StandbyTime2 小时前
《算法笔记》学习记录-第一章
c++·算法·算法笔记
近津薪荼2 小时前
优选算法——双指针8(单调性)
数据结构·c++·学习·算法
f狐0狸x2 小时前
【C++修炼之路】C++ list容器基本用法详解
开发语言·c++·list
wWYy.2 小时前
C++-集群聊天室(2):muduo网络库
网络·c++
从此不归路3 小时前
Qt5 进阶【13】桌面 Qt 项目架构设计:从 MVC/MVVM 到模块划分
开发语言·c++·qt·架构·mvc
历程里程碑3 小时前
Linux15 进程二
linux·运维·服务器·开发语言·数据结构·c++·笔记
HAPPY酷3 小时前
构建即自由:一份为创造者设计的 Windows C++ 自动化构建指南
开发语言·c++·ide·windows·python·策略模式·visual studio