当前位置: 首页 > news >正文

C++:类和对象(中)

        前面已经初步认识了C++的类和对象。简单来说,类相当于一个壳子,将成员函数和成员变量封装起来。如果你觉得C++中的类不过如此,只是把C语言中的结构体多加了一个成员函数,那你就大错特错了。本篇文章,我们将学习类中的默认成员函数。

目录

一、类的默认成员函数

 1.1、默认成员函数的概念

1.2、默认成员函数的分类

二、构造函数

2.1、构造函数的概念(干什么用的?)

2.2、构造函数的原型(语法形式)

2.3、构造函数的注意事项(怎么用)

2.4、默认构造函数

三、析构函数

3.1、析构函数的概念

3.2、析构函数的语法形式

3.3、注意事项

四、拷贝构造函数

4.1、拷贝构造函数的基本概念

4.2、基本语法

4.3、注意事项

五、赋值运算符重载

5.1、运算符重载

5.2、赋值运算符重载


一、类的默认成员函数

 1.1、默认成员函数的概念

        在类中,如果你没有显示定义某些成员函数(就是你没有写出来的某些函数),编译器会自动生成相关的函数,成为默认成员函数。

1.2、默认成员函数的分类

        共有六种:构造函数、析构函数、拷贝构造函数、拷贝赋值运算符、移动构造函数、移动赋值预算符。(最后两个函数为C++11引入的)

二、构造函数

2.1、构造函数的概念(干什么用的?)

        构造函数是特殊的成员函数,其作用是对象实例化时初始化对象。说人话,构造函数就是用来给你用类创建的对象初始化用的。

2.2、构造函数的原型(语法形式)

classname( 参数 )
{//…………
}

注意:构造函数的名字不能随便取,构造函数的名字必须是类的名字!!!

2.3、构造函数的注意事项(怎么用)

(1)构造函数的名字必须是类的名字,重要的事情要多说几遍!

(2)构造函数无返回值,连前面的void也不能写。因为不返回返回值也是一种返回值;

(3)构造函数可以重载;

(4)对象实例化时,系统会自动调用对应的构造函数

下面举个例子:

using namespace std;
class student
{
private:string _name;int _number;int _age;
public:student(){_name = "张三";_number = 2023001;_age = 18;}//构造函数重载student(string name, int number, int age){_name = name;_number = number;_age = age;}//打印学生信息void StudentPrint(){cout << "学生姓名:" << _name << endl;cout << "学生学号:" << _number << endl;cout << "学生年龄:" << _age << endl;}
};
int main()
{student s1;student s2("李四" ,2023005 ,19);s1.StudentPrint();s2.StudentPrint();return 0;
}

注意,在你用类创建对象(实例化)的时候,编译器会自动调用构造函数,所以,构造函数的传参直接在对象后面加个括号传。比如:student s2("李四" ,2023005 ,19);

(5)只有你自己不写构造函数的时候,编译器才会替你写,一旦你自己编写构造函数,编译器就不再生成构造函数了;

(6)对于编译器自己生成的构造函数,是有缺陷的:对内置类型成员变量的初始化没有要求。换句话说,是否初始化时不确定的,主要看编译器的种类。

2.4、默认构造函数

        把编译器自己生成的构造函数,我们自己写的无参构造函数,全缺省构造函数统一称为默认构造函数。主包这里概括一下,就是不用你传参构造函数也能运行的就是默认构造函数

这三个默认构造函数同时只能出现一个,不能同时存在。

三、析构函数

3.1、析构函数的概念

        析构函数也是一个特殊的成员函数,当对象声明周期结束时,自动执行必要的清理工作。主要是清理对象在生命周期内申请的内存等资源,防止泄露。

3.2、析构函数的语法形式

~classname()
{//…………
}

析构函数的名字是类的名字前加上一个~ ,必须是这样!!!

3.3、注意事项

(1)析构函数的名字必须是 ~ 加上类的名字;

(2)析构函数没有返回值,void也不能写;

(3)析构函数不能接收参数;

(4)析构函数在一个类中只能有一个,不能重载;

(5)对象生命周期结束时,系统会自动调用析构函数;

(6)对于系生成的默认构造函数,对内置类型不做处理;

(7)如果类中没有申请资源,那么析构函数可以不写,直接让编译器自己生成好了;

(8)一个局部域的多个对象,C++规定后定义的先析构。

下面通过栈来演示一下析构函数:

typedef int StackDataType;
class Stack
{
private:StackDataType* _arr;int _top;int _capacity;
public:Stack(){_arr = nullptr;_top = _capacity = 0;}//入栈void StackPush(StackDataType x){if (_top == _capacity){int newCapacity = _capacity == 0 ? 4 : 2 * _capacity;StackDataType* tmd = (StackDataType*)realloc(_arr, sizeof(StackDataType) * newCapacity);if (tmd == nullptr){perror("realloc fail!");exit(1);}_arr = tmd;_capacity = newCapacity;}_arr[_top++] = x;}//析构函数~Stack(){free(_arr);_arr = nullptr;_top = _capacity = 0;}
};

四、拷贝构造函数

4.1、拷贝构造函数的基本概念

        如果一个构造函数的第一个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数也叫拷贝构造函数。说人话,拷贝构造函数是用一个已存在的对象(拷贝源)来初始化另一个对象(拷贝对象)。

4.2、基本语法

classname( classname& other)
{//…………
}

拷贝构造函数的名字必须是类的名字,且第一个参数必须是类的引用!!!

4.3、注意事项

(1)拷贝构造函数是构造函数的重载;

(2)拷贝构造函数的第一个参数必须是当前类类型对象的引用,后面的参数必须有缺省值;

(3)C++规定自定义类型对象进行拷贝行为必须调用拷贝构造函数,所以自定义类型的传值传参和传值返回都会调用拷贝构造函数;

(4)若为定义显示拷贝构造函数,编译器会自己生成一个拷贝构造函数;

(5)如果一个类显式实现了析构并释放了资源,那么他就需要显式拷贝构造函数,否则就不需要;

(6)传值返回会产生一个临时对象的拷贝构造,传值引用返回,返回的是对象的别名,没有产生拷贝。

下面举个例子(以栈为例):

Stack(const Stack& st)
{_arr = (StackDataType*)malloc(sizeof(StackDataType) * st._capacity);if (_arr == nullptr){perror("malloc fail!");exit(1);}_top = st._top;_capacity = st._capacity;memcpy(_arr, st._arr, sizeof(StackDataType) * _top);
}

调用拷贝构造函数的用法也很简单:

Stack s1;
Stack s2 = s1;
Stack s3(s1);   //两种方式均可以

五、赋值运算符重载

5.1、运算符重载

        主包这里不想copy这个冗杂不好理解的定义了,主包要自己定义。运算符重载就是赋予C++运算符(如 +, -, =, ==, [] 等)操作自定义类型(类或结构体)的能力,让它们用起来和内置类型(如 int, double)一样直观。

什么意思?比如一开始我们创建的Student这个类,你拿两个学生相减 s1 - s2 算怎么回事?编译器无法理解,这时候你可以通过特殊手段赋予减号一定的含义,比如:两个学生的成绩之差。

class Student
{
private:string _name;int _number;int _age;float score;
public:float operator(Student& s1){return _score - s1._score;}
};

下面有几个注意事项:

(1)运算符重载是一个具有特殊名字的函数,它的名字由 operator 和后面的运算符组成;

(2)如果一个重载运算符是类的成员函数,则它的第一个运算对象默认传给 this 指针,所以在类里面的重载运算符定义时可以少写一个参数;

(3)运算符重载不改变它的优先级和结合性;

(4) .*   ::    sizeof   ?:    .    这五个运算符不能重载;

(5)重载运算符至少要给一个类型的参数;

(6)++ 有前置 ++a和 后置 a++,为了区分这两个的重构,C++规定,后置++需要增加一个int形参。

int operator++()
{cout << " ++a" << endl;return _age+1;
}
//后置++,需要跟一个int形参
int operator++(int)
{cout << " a++" << endl;return _age+1;
}

5.2、赋值运算符重载

        主包老毛病又犯了,不想看那些冗长且复杂的定义了,主包要自己定义。赋值运算符重载 (operator=) 让你能像内置类型(如 int)一样,用等号 (=) 将一个对象的值“拷贝”给另一个已存在的同类型对象。

        赋值运算重载符是一个默认成员函数,用于完成两个已经存在的对象的拷贝赋值。

student& operator=(const student& s1)
{if (this != &s1){_age = s1._age;_name = s1._name;_number = s1._number;_score = s1._score;}return *this;
}

注意,这里加一个 if 条件是为了处理有些人闲的让自己给自己赋值。返回*this 的引用是为了处理连续赋值的情况,s1 = s2 = s3 ,因为如果不返回,那么s2的值就无法得到,返回引用是为了避免再次创建临时空间调用拷贝构造函数,提高效率。

注意事项:

(1)赋值运算符重载规定必须重载为成员函数;

(2)有返回值,推荐写成当前类类型引用,提高效率;

(3)没有显示实现赋值运算符重载时,编译器会自己生成一个默认赋值运算符重载,对内置类型成员变量,对内置类型成员变量完成值拷贝(浅拷贝)。

        


文章转载自:

http://7zhfPZ50.rdnjc.cn
http://uPwrVopc.rdnjc.cn
http://o72APrxz.rdnjc.cn
http://LQXhM3RT.rdnjc.cn
http://KcMkuztC.rdnjc.cn
http://7mjYRKvv.rdnjc.cn
http://HtQIY3BV.rdnjc.cn
http://NhzXkwr9.rdnjc.cn
http://HQ4N7tRE.rdnjc.cn
http://M365JbhV.rdnjc.cn
http://umINUJ9e.rdnjc.cn
http://Je8FmoSp.rdnjc.cn
http://14MdXDFD.rdnjc.cn
http://XQze1QUG.rdnjc.cn
http://qCeAiaxF.rdnjc.cn
http://J4SBek2l.rdnjc.cn
http://QssnqZs6.rdnjc.cn
http://c6mCALcF.rdnjc.cn
http://xj4QMX2t.rdnjc.cn
http://8ubmrElE.rdnjc.cn
http://MBJfQ0NF.rdnjc.cn
http://WCdgb4Wt.rdnjc.cn
http://r7icwBIH.rdnjc.cn
http://BPa5wIIS.rdnjc.cn
http://G46pJIaU.rdnjc.cn
http://Xcinp0Ji.rdnjc.cn
http://JjsTBvZ3.rdnjc.cn
http://4fDXFiTQ.rdnjc.cn
http://EG5wzL9m.rdnjc.cn
http://Sqc9o4xl.rdnjc.cn
http://www.dtcms.com/a/367095.html

相关文章:

  • 25高教社杯数模国赛【C题国一亲授思路+问题解析】第四弹
  • 【数学建模学习笔记】机器学习回归:K邻近回归
  • JavaEE 进阶第二期:开启前端入门之旅(二)
  • 准确率可达99%!注意力机制+UNet,A会轻松收割!
  • SpringBoot 项目一些语法记录
  • 单通道ADC采集实验(单次非扫描软件触发)
  • 同步安卓手机的照片到NAS的方案(完美)
  • 嵌入式设备的外设驱动优化
  • 51单片机---硬件学习(跑马灯、数码管、外部中断、按键、蜂鸣器)
  • 嵌入式 - 硬件:51单片机(3)uart串口
  • 深度剖析:智能驾驶到底给2025带来了什么
  • MTK Linux DRM分析(三十六)- MTK mtk_cec.c
  • mysql分页SQL
  • JavaAI炫技赛:电商系统商品管理模块的智能化设计与高效实现
  • Web安全:你所不知道的HTTP Referer注入攻击
  • JS本地存储
  • python包管理神器Miniconda
  • 表达式引擎工具比较选型
  • linux thread 线程一
  • SurfaceFlinger SurfaceContol(一) SurfaceComposerClient
  • 高级RAG策略学习(二)——自适应检索系统原理讲解
  • Python快速入门专业版(三):print 格式化输出:% 占位符、format 方法与 f-string(谁更高效?)
  • 2025打磨机器人品牌及自动化打磨抛光设备技术新版分析
  • 只会git push?——git团队协作进阶
  • Ubuntu系统配置镜像源
  • RTSP H.265 与 RTMP H.265 的差异解析:标准、扩展与增强实现
  • Vue基础知识-脚手架开发-子传父(props回调函数实现和自定义事件实现)
  • 九、数据库技术基础
  • Roo Code之自定义指令(Custom Instructions),规则(Rules)
  • 掌握DNS解析:从基础到BIND部署全解析