目录

 

4.2 对象特征

4.2.1 构造函数和析构函数

4.2.2 构造函数的分类

4.2.3 拷贝函数调用时机

4.2.4 构造函数调用规则

4.2.5 深拷贝与浅拷贝

4.2.6 初始化列表

4.2.7 类对象作为类成员

4.2.8 静态成员

4.2.9 成员变量和成员函数的存储

4.2.10 this指针

4.2.11 空指针访问成员函数

4.2.12 const修饰成员函数


4.2 对象特征

对象的初始化和清理:C++中,每个对象都有初始设置和对象销毁前的清理数据的设置。

4.2.1 构造函数和析构函数

C++中利用构造函数和析构函数对对象进行初始化和清理。这两个函数会被编译器自动调用,完成对象的初始化和清理。如果程序员不提供构造和析构,编译器会提供构造函数和析构函数,但是是空的。

构造函数:创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

语法:类名(){}

1.构造函数,没有返回值也不写void;

2.函数名与类名相同;

3.构造函数可以有参数,因此可以发生重载;

4.程序在调用对象时会自动调用构造,无须手动调用,且只调用一次。

析构函数:对象销毁前系统自动调用,执行一些清理工作。

语法:~类名(){}

1.析构函数,没有返回值也不写void;

2.函数名与类名相同,在函数名前加上符号~;

3.析构函数不可以有函数,因此不可以发生重载;

4.程序在对象销毁前会自动调用析构,无须手动调用,且只调用一次。

代码如下:

#include <iostream>
using namespace std;class Person
{
public://构造函数 初始化对象Person(){cout<<"Person构造函数的调用"<<endl;}//析构函数 销毁/清理对象~Person(){cout<<"Person析构函数的调用"<<endl;}
};void test01()
{Person p;//栈上的数据,该函数执行完后,p这个对象会被释放
}int main()
{//对象的初始化和清理test01();Person p;system("pause");return 0;
}

输出如下:

4.2.2 构造函数的分类

按参数分:有参构造、无参构造;

按类型分:普通构造、拷贝构造。

调用方式:括号法、显示法、隐式转换法。

代码如下:

#include <iostream>
using namespace std;//构造函数发分类及调用
class Person
{
public://无参(默认)构造Person(){cout<<"Person的无参构造函数的调用"<<endl;}//有参构造Person(int a){age=a;cout<<"Person的有参构造函数的调用"<<endl;}//拷贝构造函数(将Person p的属性拷贝过来)Person(const Person &p){age=p.age;cout<<"Person的拷贝构造函数的调用"<<endl;}~Person(){cout<<"Person析构函数的调用"<<endl;}private:int age;
};void test01()
{//调用:括号法cout<<"括号法调用构造函数:"<<endl;Person p1;//默认构造函数调用,不用加括号,编译器会认为Person p1();是一个函数声明。Person p2(21);//有参构造函数调用Person p3(p2);//拷贝构造函数调用//调用:显示法cout<<"显示法调用构造函数:"<<endl;Person p4;Person p5=Person(21);//有参构造Person P6=Person(p5);//拷贝构造Person(21);//表示一个匿名对象,在等式左边的P2就是给他取的名字,匿名对象执行后会立即回收。cout<<"匿名对象清理后执行了这句代码"<<endl;//PS:不要用拷贝构造 初始化匿名对象,编译器会认为Person(p3);是一个对象声明Person p3;//Person(p3);//调用:隐式转换法cout<<"显示法调用构造函数:"<<endl;Person p7=21;//有参构造Person p8=p7;//拷贝构造
}int main()
{test01();return 0;
}

输出如下:

4.2.3 拷贝函数调用时机

  • 使用一个已经创建完毕的对象来初始化一个新对象;
  • 值传递的方式给函数参数传值;
  • 以值方式返回局部对象。

代码如下:

#include <iostream>
using namespace std;//拷贝构造函数调用时机
class Person
{
public://无参(默认)构造Person(){cout<<"Person的无参构造函数的调用"<<endl;}//有参构造Person(int a){age=a;cout<<"Person的有参构造函数的调用"<<endl;}//拷贝构造函数Person(const Person &p){age=p.age;cout<<"Person的拷贝构造函数的调用"<<endl;}~Person(){cout<<"Person析构函数的调用"<<endl;}int age;
};//使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{cout<<"test01函数调用"<<endl;Person p1(21);Person p2(p1);cout<<"p2的年龄为:"<<p2.age<<endl;
}//值传递的方式给函数参数传值
void doWork(Person p)
{}void test02()
{cout<<"test02函数调用"<<endl;Person p;doWork(p);//这里传入的p和dowork中的p不一样
}//以值方式返回局部对象
Person doWork2()
{Person p1;cout<<"p1的地址为:"<<(long long)&p1<<endl;return Person(p1);//直接返回p1则不会调用拷贝函数,因为编译器自动做了优化(可以看到p1和p的地址一样)
}void test03()
{cout<<"test03函数调用"<<endl;Person p=doWork2();cout<<"p的地址为:"<<(long long)&p<<endl;
}int main()
{test01();test02();test03();return 0;
}

输出如下:

4.2.4 构造函数调用规则

默认情况下,C++编译器至少给类添加三个函数;

  1. 1.默认构造函数(无参,函数体为空)
  2. 2.默认析构函数(无参,函数体为空)
  3. 3.默认拷贝构造函数,对属性进行值拷贝

调用规则:

  • 如果用户定义了有参构造函数,则编译器不提供默认无参构造,但会提供默认拷贝构造
  • 如果用户定义了拷贝构造函数,则编译器不再提供其他构造函数

代码如下:

#include <iostream>
using namespace std;class Person
{
public:// Person()// {//     cout<<"person的默认构造函数"<<endl;// }Person(int a){age=a;cout<<"Person的有参构造函数的调用"<<endl;}// Person(const Person &p)// {//     age=p.age;//     cout<<"Person的拷贝构造函数的调用"<<endl;// }~Person(){cout<<"Person析构函数的调用"<<endl;}int age;
};// void test01()
// {
//     Person p;
//     p.age=18;//     Person p2(p);
//     cout<<"p2的年龄为:"<<p2.age<<endl;
// }void test02()
{Person p(28);Person p2(p);cout<<"p2的年龄为:"<<p2.age<<endl;
}int main()
{//test01();test02();return 0;
}

输出如下:用户定义了拷贝构造函数

输出如下:用户没有定义拷贝构造函数

错误示例:用户定义了有参构造,但没有定义无参(默认)构造,则编译器也不会提供默认构造,此时调用默认构造则会报错。

输出如下:用户只定义了有参构造,则编译器依然或提供拷贝构造

4.2.5 深拷贝与浅拷贝

浅拷贝:编译器提供的拷贝函数,简单的赋值拷贝操作;

缺点:容易导致堆区的重复释放,利用深拷贝解决。

深拷贝:在堆区重新申请空间,进行拷贝操作,而不是与被拷贝的指针指向相同的空间。

PS:如果属性有在堆区开辟的,一定要自己定义拷贝构造函数,防止浅拷贝中出现的问题。

代码如下:

#include <iostream>
using namespace std;class Person
{
public:Person(){cout<<"person的默认构造函数"<<endl;}Person(int a,int h){age=a;height=new int(h);cout<<"Person的有参构造函数的调用"<<endl;}//自己实现拷贝构造函数,解决浅拷贝的问题Person(const Person &p){age=p.age;height=p.height;//编译器写的(浅拷贝)height= new int(*p.height);//深拷贝操作,另外开辟空间cout<<"Person的拷贝构造函数的调用"<<endl;}~Person(){//析构的作用,将堆区new的数据手动释放if(height!=NULL)//若指针不为空,则需要释放{delete height;//P2先释放,完了之后P也需要释放,但两个对象的指针操作的是同一个堆区中的地址,造成重复释放的非法操作,因此会报错height=NULL;//防止野指针出现,将指针置空}cout<<"Person析构函数的调用"<<endl;}int age;int * height;
};void test01()
{Person p(28,160);Person p2(p);cout<<"p2的年龄为:"<<p2.age<<" 身高为:"<<*p2.height<<endl;
}int main()
{test01();return 0;
}

输出如下:

4.2.6 初始化列表

作用:C++提供了初始化列表语法,用来初始化属性。

语法:构造函数():属性1(值1),属性2(值2)...{}

代码如下:

#include <iostream>
using namespace std;class Person
{
public://传统初始化操作// Person(int a,int b,int c)// {//     A=a;//     B=b;//     C=c;// }//初始化列表赋初值//Person():A(1),B(2),C(3){}Person(int a,int b,int c):A(a),B(b),C(c){}int A;int B;int C;
};void test01()
{//Person p(10,20,30);//传统赋值Person p(1,2,3);//列表赋值cout<<"A="<<p.A<<endl;cout<<"B="<<p.B<<endl;cout<<"C="<<p.C<<endl;}int main()
{test01();return 0;
}

输出如下:

4.2.7 类对象作为类成员

C++中类的成员可以是另一个类的对象,称为对象成员。

注意对象作为成员时,两种对象的构造和析构函数的顺序。(先构造其他类,再构造本类,先析构本类,再析构其他类)

代码如下:

#include <iostream>
using namespace std;
#include <string>//对象成员
class Phone
{
public://手机品牌string PName;Phone(string pname){cout<<"Phone的构造函数的调用"<<endl;PName=pname;}~Phone(){cout<<"Phone析构函数的调用"<<endl;}
};class Person
{
public://P(pname)相当于Phone P=pname; 隐式转换法Person(string name,string pname):Name(name),P(pname){cout<<"Person的构造函数的调用"<<endl;}~Person(){cout<<"Person析构函数的调用"<<endl;}string Name;Phone P;
};void test01()
{Person p("张三","iPhone18");cout<<p.Name<<"拿着"<<p.P.PName<<endl;
}int main()
{test01();return 0;
}

输出如下:

4.2.8 静态成员

静态成员是指在成员变量和成员函数卡加static关键字,静态成员都有三种访问权限。

静态成员变量:

  • 所有对象共享同一份数据
  • 在编译阶段分配内存(程序运行前)
  • 类内声明,类外初始化

静态成员函数

  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量

代码如下:

#include <iostream>
using namespace std;
#include <string>//静态成员
class Person
{
public://静态成员变量static int A;//类内声明int B;//静态成员函数static void func(){A=44;//B=22;//静态成员函数访问非静态成员变量,报错,无法区分是哪个对象的Bcout<<"静态成员函数调用"<<endl;}
};//类外初始化
int Person::A=100;void test01()
{Person p;cout<<p.A<<endl;Person p2;p2.A=200;//所有对象共享同一份数据,因此有两种访问方式:通过对象访问;通过类名访问cout<<p.A<<endl;cout<<Person::A<<endl;
}void test02()
{//两种访问方式:通过对象访问;通过类名访问Person p;p.func();Person::func();cout<<p.A<<endl;
}int main()
{test01();test02();return 0;
}

输出如下:

错误示例:静态成员函数访问非静态成员变量

4.2.9 成员变量和成员函数的存储

类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上的。

代码如下:

#include <iostream>
using namespace std;
#include <string>//静态成员
class Person1
{};class Person2
{int A;//非静态成员变量 
};class Person3
{int A;static int B;//静态成员变量
};int Person3::B=9;class Person4
{int A;static int B;void func(){}//非静态成员函数
};class Person5
{int A;static int B;void func(){}//非静态成员函数static void func2(){};
};void test01()
{Person1 p1;//空对象占用内存为1,为了区分空对象占内存的位置,每个空对象有一个唯一的地址cout<<"size of p1="<<sizeof(p1)<<endl;Person2 p2;//有非静态成员变量,占4字节  属于类的对象上的数据cout<<"size of p2="<<sizeof(p2)<<endl;Person3 p3;//有静态成员变量  不属于类的对象上的数据cout<<"size of p3="<<sizeof(p3)<<endl;Person4 p4;//非静态成员函数  不属于类的对象上的数据cout<<"size of p4="<<sizeof(p4)<<endl;Person5 p5;//静态成员函数  不属于类的对象上的数据cout<<"size of p5="<<sizeof(p5)<<endl;
}int main()
{test01();return 0;
}

输出如下:

4.2.10 this指针

每一个非静态成员函数只会产生一个函数实例,所有同类中的多个对象会公用一块代码。

C++提供this指针来指向被调用的成员函数所属的对象。this指针是隐含每一个非静态成员函数内的一种指针,不需要定义,直接使用即可。

用途:

  • 当形参和成员变量同名时,用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this。

PS:用Person&定义返回值类型,是因为可以一直对同一个空间操作,用Person定义返回值类型表示值返回,会复制一份新的数据(按照本体p2创建了新的数据,而不是返回的p2本体),调用了拷贝构造函数。

代码如下:

#include <iostream>
using namespace std;
#include <string>class Person
{
public:int age;Person(int age){//age=age;//报错//this指针指向被调用的成员函数所属的对象p1this->age=age;}
//用Person&定义返回值类型,是因为可以一直对同一个空间操作,用Person定义返回值类型表示值返回,会复制一份新的数据(按照本体p2创建了新的数据,而不是返回的p2本体),调用了拷贝构造函数Person& PersonAddAge(Person &p){this->age+=p.age;//this指向p2的指针,*p2指向p2本体return *this;}
};//解决名称冲突
void test01()
{Person p1(18);cout<<p1.age<<endl;
}//用*this 返回对象本身
void test02()
{Person p1(31);Person p2(31);p2.PersonAddAge(p1);cout<<p2.age<<endl;p2.PersonAddAge(p1).PersonAddAge(p1);//用this*返回才能链式追加cout<<p2.age<<endl;}int main()
{test01();test02();return 0;
}

输出如下:

错误示例:名称冲突,形参和属性名相同时,不能输出正确结果

4.2.11 空指针访问成员函数

C++中空指针可以调用成员函数,但需要注意有没有用this指针。如果用到this指针,需要加以判断保证代码的健壮性。

代码如下:

#include <iostream>
using namespace std;//空指针调用成员函数
class Person
{
public:void showClassName(){cout<<"this is person class"<<endl;}void showPersonAge(){if(this==NULL){return;}//传入指针为空,报错  在前面加一个空指针的判断cout<<"age="<<this->age<<endl;}int age;
};void test01()
{Person *p=NULL;p->showClassName();p->showPersonAge();
}int main()
{test01();return 0;
}

输出如下:

错误示例:用空指针访问属性,图中age,默认是this->age,而访问时用的空指针,this为空所以不能指向正确的对象的属性。

4.2.12 const修饰成员函数

常函数:

  • 成员函数后加const后称为常函数;
  • 常函数内不可用修改成员属性;
  • 成员属性声明时加关键词mutable后,在常函数中依然可以修改

常对象:

  • 声明对象前加const称该对象为常对象;
  • 常对象不允许修改指针指向的值;

  • 常对象只能调用常函数

代码如下:

#include <iostream>
using namespace std;//常函数
class Person
{
public://this指针的本质是一个指针常量Person * const this 指针的指向是不可修改的//后面加的const相当于const Person * const this,使this指向的值也不可修改void showPerson() const{this->b=99;//this=NULL;//this的指针指向不能修改cout<<"this is person class"<<endl;}Person(){}//不写默认构造函数会报错实例化的常对象没有初始化void func(){}int age;mutable int b;
};void test01()
{Person p;p.showPerson();
}void test02()
{const Person p;//p.age=99;//报错 常对象不允许修改指针指向的值p.b=88;p.showPerson();//p.func();//报错 常对象不能调用非常函数
}int main()
{test01();test02();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/897499.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/897499.shtml
英文地址,请注明出处:http://en.pswp.cn/news/897499.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL_04】数据库基本操作(用户管理--配置文件--远程连接--数据库信息查看、创建、删除)

文章目录 一、MySQL 用户管理1.1 用户管理1.11 mysql.user表详解1.12 添加用户1.13 修改用户权限1.14 删除用户1.15 密码问题 二、MySQL 配置文件2.1 配置文件位置2.2 配置文件结构2.3 常用配置参数 三、MySQL远程连接四、数据库的查看、创建、删除4.1 查看数据库4.2 创建、删除…

配置 Thunderbird 以使用 outlook 邮箱

配置 Thunderbird 以使用 outlook 邮箱 thunder bird 作为邮件客户端非常好用&#xff0c;不用每次登录邮箱网页端查看邮件&#xff0c;直接打开配置好的 thunder bird 即可免登录查看邮件。 0. 什么是 Thunder Bird ? https://www.thunderbird.net/zh-CN/ Thunderbird 创立…

边缘计算的业务种类划分

Pcdn的业务可以根据不同的分类标准来划分 一、按线路类型划分 汇聚模式&#xff1a;一个地方有多条线路&#xff0c;业务种类较多。通常使用X86或X99主板组装的服务器&#xff0c;或各品牌的准系统服务器。收益通常比单线模式更高。 单线模式&#xff1a;一个地方只有一条线路&…

服务器数据恢复—raid5阵列中硬盘出现坏道的数据恢复流程

服务器故障情况&#xff1a; 某公司一台服务器中有一组多块硬盘组成的磁盘阵列。磁盘阵列中有2块硬盘出现故障离线&#xff0c;服务器崩溃&#xff0c;上层数据丢失。 硬件检测&#xff1a; 硬件工程师对客户服务器内的所有硬盘进行物理故障检测&#xff0c;最终确认这2块硬盘…

Linux:多线程(三.POSIX信号量、生产消费模型、线程池)

目录 1. 生产者消费者模型 1.1 阻塞队列(BlockingQueue) 1.2 一个实际应用的例子 2. POSIX信号量 2.1 引入 2.2 回顾加深理解信号量 2.3 信号量的操作接口 3. 基于循环队列的生产消费模型 3.1 循环队列 3.2 整个项目 4. 线程池 4.1 概念 4.2 线程池实现 1. 生产者…

关于前后端整合和打包成exe文件的个人的总结和思考

前言 感觉有很多东西&#xff0c;不知道写什么&#xff0c;随便写点吧。 正文 前后端合并 就不说怎么开发的&#xff0c;就说点个人感觉重要的东西。 前端用ReactViteaxios随便写一个demo&#xff0c;用于CRUD。 后端用Django REST Framework。 设置前端打包 import { …

Android15 Camera框架中的StatusTracker

StatusTracker介绍 StatusTracker是Android15 Camera框架中用来协调Camera3各组件之间状态转换的类。 StatusTracker线程名&#xff1a;std::string("C3Dev-") mId "-Status" Camera3 StatusTracker工作原理 StatusTracker实现批处理&#xff08;状态…

利用OpenResty拦截SQL注入

需求 客户的一个老项目被相关部门检测不安全&#xff0c;报告为sql注入。不想改代码&#xff0c;改项目&#xff0c;所以想到利用nginx去做一些数据校验拦截。也就是前端传一些用于sql注入的非法字符或者数据库的关键字这些&#xff0c;都给拦截掉&#xff0c;从而实现拦截sql…

警惕AI神话破灭:深度解析大模型缺陷与禁用场景指南

摘要 当前AI大模型虽展现强大能力&#xff0c;但其本质缺陷可能引发系统性风险。本文从认知鸿沟、数据困境、伦理雷区、技术瓶颈四大维度剖析大模型局限性&#xff0c;揭示医疗诊断、法律决策等8类禁用场景&#xff0c;提出可信AI建设框架与用户防护策略。通过理论分析与实操案…

颠覆语言认知的革命!神经概率语言模型如何突破人类思维边界?

颠覆语言认知的革命&#xff01;神经概率语言模型如何突破人类思维边界&#xff1f; 一、传统模型的世纪困境&#xff1a;当n-gram遇上"月光族难题" 令人震惊的案例&#xff1a;2012年Google语音识别系统将 用户说&#xff1a;“我要还信用卡” 系统识别&#xff…

【Linux】详谈 基础I/O

目录 一、理解文件 狭义的理解&#xff1a; 广义理解&#xff1a; 文件操作的归类认知 系统角度 二、系统文件I/O 2.1 标志位的传递 系统级接口open ​编辑 open返回值 写入文件 读文件 三、文件描述符 3.1&#xff08;0 & 1 & 2&#xff09; 3.2 文件描…

超分之DeSRA

Desra: detect and delete the artifacts of gan-based real-world super-resolution models.DeSRA&#xff1a;检测并消除基于GAN的真实世界超分辨率模型中的伪影Xie L, Wang X, Chen X, et al.arXiv preprint arXiv:2307.02457, 2023. 摘要 背景&#xff1a; GAN-SR模型虽然…

Vue3 Pinia 符合直觉的Vue.js状态管理库

Pinia 符合直觉的Vue.js状态管理库 什么时候使用Pinia 当两个关系非常远的组件&#xff0c;要传递参数时使用Pinia组件的公共参数使用Pinia

Web Worker如何在本地使用

首先了解一下什么是Web Worker Web Worker 是一种在后台线程中运行 JavaScript 的机制&#xff0c;允许你在不阻塞主线程的情况下执行耗时的任务。这对于保持网页的响应性和流畅性非常重要&#xff0c;特别是在需要进行复杂计算或大量数据处理时。 主要特点 多线程&#xff1…

Javaweb后端文件上传@value注解

文件本地存储磁盘 阿里云oss准备工作 阿里云oss入门程序 要重启一下idea&#xff0c;上面有cmd 阿里云oss案例集成 优化 用spring中的value注解

MAC-禁止百度网盘自动升级更新

通过终端禁用更新服务(推荐)​ 此方法直接移除百度网盘的自动更新组件,无需修改系统文件。 ​步骤: ​1.关闭百度网盘后台进程 按下 Command + Space → 输入「活动监视器」→ 搜索 BaiduNetdisk 或 UpdateAgent → 结束相关进程。 ​2.删除自动更新配置文件 打开终端…

数据结构:有序表的插入

本文是我编写的针对计算机专业考研复习《数据结构》所用资料内容选刊。主要目的在于向复习这门课程的同学说明&#xff0c;此类问题不仅仅使用顺序表&#xff0c;也可以使用链表。并且&#xff0c;在复习中&#xff0c;两种数据结构都要掌握。 若线性表中的数据元素相互之间可以…

DeepSeek大语言模型下几个常用术语

昨天刷B站看到复旦赵斌老师说的一句话“科幻电影里在人脑中植入芯片或许在当下无法实现&#xff0c;但当下可以借助AI人工智能实现人类第二脑”&#xff08;大概是这个意思&#xff09; &#x1f49e;更多内容&#xff0c;可关注公众号“ 一名程序媛 ”&#xff0c;我们一起从 …

Html5学习教程,从入门到精通, HTML5超链接应用的详细语法知识点和案例代码(18)

HTML5超链接应用的详细语法知识点和案例代码 超链接&#xff08;Hyperlink&#xff09;&#xff0c;也称为跃点链接&#xff0c;是互联网和文档编辑中的一种重要概念。 超链接的定义 超链接是指从一个网页指向一个目标的连接关系&#xff0c;这个目标可以是另一个网页&#…

MYSQL之创建数据库和表

创建数据库db_ck &#xff08;下面的创建是最好的创建方法&#xff0c;如果数据库存在也不会报错&#xff0c;并且指定使用utf8mb4&#xff09; show databases命令可以查看所有的数据库名&#xff0c;可以找到刚刚创建的db_ck数据库 使用该数据库时&#xff0c;发现里面没有…