类与对象-对象特性

 师从黑马程序员

对象的初始化和清理

构造函数和析构函数

用于完成对象的初始化和清理工作

如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现

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

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

#include <iostream>using namespace std;
//1、构造函数  进行初始化操作
class Person
{
public://1.1、构造函数//没有返回值 不用写void//函数名 与类名相同//构造函数可以有参数,可以发生重载//创建对象的时候,构造函数会自动调用,而且只调用一次Person(){cout<<"Person 构造函数的调用"<<endl;}//2、析构函数 进行清理的操作//没有返回值 不用写void//函数名 与类名相同   名称前加~//析构函数不可以有参数,不可以发生重载//对象在销毁前,析构函数会自动调用,而且只调用一次~Person(){cout<<"Person 的析构函数调用"<<endl;}
};
//构造和析构都是必须有的实现,如果我们自己不提供,编译器会提供一个空实现的构造和析构void test01()
{Person p;//在栈上的数据,test01执行完毕后,释放这个对象
}
int main()
{test01();//Person 构造函数的调用//Person 的析构函数调用Person p;//Person 构造函数的调用system("pause");return 0;
}

构造函数的分类及调用

两种分类方式:

        按参数分为:有参构造和无参构造

        按类型分为:普通构造和拷贝构造

三种调用方式:

        括号法        显示法        隐式转化法

#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;}public:int age; // 增加成员变量age
};
//调用无参构造函数
void test01(){Person p;
}// 调用有参构造函数
void test02()
{//1.括号法  常用Person p1; // 无参构造函数的调用Person P2(10); // 有参构造函数的调用Person P3(P2); // 拷贝构造函数的调用//注意事项//调用无参构造函数时,不要加()//因为下面这行代码,编译器会认为一个函数的声明,不会认为在创建对象//Person p1();//显示法// Person p1;Person p2=Person(10);//有参构造Person p3=Person(p2);//拷贝构造、Person(10);//匿名对象 特点:当前执行结束后,系统会立即回收掉匿名对象cout<<"aaaa"<<endl;//Person有参构造函数的调用//Person析构造函数的调用//aaaa//注意事项2//不要用拷贝构造函数  初始化匿名对象 编译器会认为Person (p3)==Person  p3;对象会重复定义//Person (p3);//3.隐式转化法Person p4=10;//相当于写了  Person p4=Person(10); //有参构造Person p5=p4;//拷贝构造
}int main()
{test01();test02();system("pause"); // (仅适用于Windows系统)return 0;
}

拷贝构造函数调用时机

使用一个已经创建完毕的对象来初始化一个新对象

值传递的方式给函数参数传值

以值方式返回局部对象

#include <iostream>using namespace std;class Person
{
public:// 无参构造函数Person(){cout << "Person 无参构造函数的调用" << endl;}// 有参构造函数Person(int age){m_Age = age;cout << "Person 有参构造函数的调用" << endl;}// 拷贝构造函数Person(const Person &p){m_Age = p.m_Age;cout << "Person 拷贝构造函数的调用" << endl;}// 析构函数~Person(){cout << "Person 的析构函数调用" << endl;}int m_Age;
};// 使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{Person p1(20);Person p2(p1);
}// 值传递的方式给函数参数传值
void doWork(const Person& p)
{}void test02()
{Person p;doWork(p);
}// 以值方式返回局部对象
void doWork2()
{Person p1 ;return p1;
}void test03()
{Person p = doWork2();int main()
{test01();test02();test03();system("pause"); // (仅适用于Windows系统)return 0;
}

构造函数调用规则

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

1、默认构造函数(无参,函数体为空)

2、默认析构函数(无参,函数体为空)

3、默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则如下:

如果用户定义有参构造函数,C++不在提供默认无参构造,但是会提供默认拷贝构造

如果用户定义拷贝构造函数,C++不会在提供其他构造函数

#include <iostream>using namespace std;class Person
{
public:// 无参构造函数/*  Person(){cout << "Person 无参构造函数的调用" << endl;}*/// 有参构造函数/* Person(int age){m_Age = age;cout << "Person 有参构造函数的调用" << endl;}*/// 拷贝构造函数/*  Person(const Person &p){m_Age = p.m_Age;cout << "Person 拷贝构造函数的调用" << endl;}
*/// 析构函数~Person(){cout << "Person 的析构函数调用" << endl;}int m_Age;
};//默认情况下,C++编译器至少给一个类添加3个函数
void test01()
{Person p;p.m_Age=18;Person p2(p);cout<<"p2的年龄为:"<<p2.m_Age<<endl;
}
//如果用户定义有参构造函数,
//C++不在提供默认无参构造,但是会提供默认拷贝构造
void test02()
{//Person p;Person p(28);Person P2(p);cout<<"p2的年龄为:"<<p2.m_Age<<endl;
}
/* wrong 
void test03()
{Person p;
}
*/
int main()
{test01();test02();system("pause"); // (仅适用于Windows系统)return 0;
}

深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作

#include <iostream>using namespace std;
//浅拷贝带来的问题是堆区的代码重复释放//深拷贝:在堆区重新申请空间,进行拷贝操作
class Person
{
public:// 无参构造函数Person(){cout << "Person 无参构造函数的调用" << endl;}// 有参构造函数Person(int age,int height){m_Age = age;m_Height=new int(height);cout << "Person 有参构造函数的调用" << endl;}// 自己实现拷贝构造函数,解决浅拷贝带来的问题Person(const Person &p){m_Age = p.m_Age;cout << "Person 拷贝构造函数的调用" << endl;//m_Height=p.m_Height;编译器默认实现就是这行代码//深拷贝操作m_Height=new int (*p.m_Height);}// 析构函数~Person(){//析构代码,将堆区开辟数据做释放操作if(m_Height !=NULL){delete m_Height;m_Height=NULL;//防止其野指针的出现}cout << "Person 的析构函数调用" << endl;}int m_Age;//年龄int *m_Height;//身高};void test01()
{Person p1(18,160);cout<<"p1的年龄为:"<<p1.m_Age<<"p1的身高为:"<<p1.m_Height<<endl;Person p2(p1);cout<<"p2的年龄为:"<<p2.m_Age<<"p1的身高为:"<<p1.m_Height<<endl;
}int main()
{test01();system("pause"); // (仅适用于Windows系统)return 0;
}

如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

初始化列表

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

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

#include <iostream>using namespace std;class Person
{
public://传统初始化操作/* Person(int a,int b,int c){m_A=a;m_B=b;m_C=c;}*///初始化列表初始化属性Person(int a,int b,int c) :m_A(a),m_B(b),m_C(c){}int m_A;int m_B;int m_C;
};void test01()
{//Person p(10,20,30);Person p(30,20,10);cout<<"m_A= "<<p.m_A<<endl;cout<<"m_B= "<<p.m_B<<endl;cout<<"m_C= "<<p.m_C<<endl;
}int main()
{test01();system("pause"); // (仅适用于Windows系统)return 0;
}

类对象作为类成员

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

例:

class A{}
class B
{A a;
}

B类中有对象A作为成员,A为对象成员

#include <iostream>
#include <string>
using namespace std;
class  Phone
{
public:Phone(string pName){cout<<"Phone 的构造函数调用"<<endl;m_PName =pName;}~Phone (){cout<<"Phone 的构造函数调用"<<endl;}string m_PName;
};
class Person
{
public:Person(string name,string pName):m_Name(name),m_Phone(pName){cout<<"Person 的构造函数调用"<<endl;}~Person (){cout<<"Person 的构造函数调用"<<endl;}//姓名string m_Name;//手机Phone m_Phone;};
//person 先析构,Phone再析构
//当其他类的对象作为本类成员,构造时候先构造类对象,再构造自身,析构的顺序与构造相反void test01()
{Person p("张三","苹果MAX");cout<<p.m_Name<<"拿着"<<p.m_Phone.m_PName<<endl;
}int main()
{test01();system("pause"); // (仅适用于Windows系统)return 0;
}

静态成员

在成员变量和成员函数前加上关键字static,称为静态成员

静态成员分为:

1、静态成员变量

所有对象共享一份数据

在编译阶段分配内存

类内声明,类外初始化

#include <iostream>using namespace std;
//静态成员变量class Person
{
public://所有对象共享一份数据static int m_A;//静态成员变量也是有访问权限的
private:static int m_B;
};//类内声明,类外初始化
int Person::m_A=100;
int Person::m_B=200;
void test01()
{Person p;cout<<p.m_A<<endl;//100//所有对象共享一份数据Person p2;p2.m_A=200;cout<<p.m_A<<endl;//200//cout<<p.m_B<<endl;wrong 类外访问不到私有静态成员变量}void test02()
{//静态成员变量 不属于某个对象上,所有对象都共享同一份数据//因此静态成员变量有两种访问方式//1、通过对象进行访问Person p;cout<<p.m_A<<endl;//200//2、通过类名进行访问cout<<Person::m_A<<endl;//200}int main()
{test01();test02();system("pause");return 0;
}

2、静态成员函数

所有对象共享同一个函数

静态成员函数只能访问静态成员变量

#include <iostream>using namespace std;class Person
{
public://静态成员函数static void func(){m_A=100;//静态成员函数可以访问静态成员函数//静态成员函数只能访问静态成员变量//m_B=200;wrong无法区分到底是哪个对象的m_Bcout<<"static void func调用"<<endl;}static int m_A;//静态成员变量int m_B;//非静态成员变量//静态成员函数也有访问权限
private:static void func2(){cout<<" static void func2调用"<<endl;}};int Person::m_A=0;void test01()
{//1、通过对象访问Person p;p.func();//2、通过类名访问Person::func();//Person::func2(); 类外访问不到私有的静态成员函数
}int main()
{test01();system("pause");return 0;
}

C++对象模型和this指针

成员变量和成员函数分开存储

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

#include <iostream>using namespace std;class Person
{int m_A;//非静态成员变量  属于类的对象上static int m_B;//静态成员变量 不属于类的对象上void func(){}//非静态成员函数 不属于类的对象上static void func2(){}//静态成员函数 不属于类的对象上
};/*
void test01()
{Person p;//空对象占用内存为1//C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置//每个空对象也应该有一个独一无二的内存地址cout<<"size of p="<<sizeof(p)<<endl;
}
*/
void test02()
{Person p;cout<<"size of p="<<sizeof(p)<<endl;//4
}int main()
{//test01();test02();system("pause");return 0;
}

this 指针的用途

this 指针指向被调用的成员函数所属的对象

this 指针是隐含每一个非静态成员函数内的一种指针

this指针不需要定义,直接使用即可

this指针的用途:

当形参和成员变量同名时,可以用this指针来区分

在类的非静态成员函数中返回对象本身,可使用return  *this

#include <iostream>using namespace std;class Person
{
public:Person(int age){//this 指针指向被调用的成员函数所属的对象this-> age =age;}Person &PersonAddAge(Person &p){this->age +=p.age;//this 指向p2的指针,而*this指向的就是p2这个对象本体return *this;}int age;
};//1、解决名称冲突
void test01()
{Person p1(18);cout<<"the p1s age is "<<p1.age<<endl;
}//2、返回对象本身使用return *this
void test02()
{Person p1(10);Person p2(10);
//链式编程思想p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);cout<<"the p2s age is "<<p2.age<<endl;
}
int main()
{test01();test02();system("pause");return 0;
}

空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意没有用到的this指针

如果用到this指针,需要加以判断保证代码的健壮性

#include <iostream>using namespace std;
//空指针调用成员函数class Person
{
public:void showClassName(){cout<<"this is Person class "<<endl;}void showPersonAge(){//报错的原因是传入的指针是NULLif(this==NULL){return ;}//属性前都默认加了this->cout<<"age ="<<this ->m_Age<<endl;}int m_Age;
};void test01()
{Person *p=NULL;p->showClassName();p->showPersonAge();
}void test02()
{}
int main()
{test01();system("pause");return 0;
}

const 修饰成员函数

常函数:

成员函数后加const后我们称为这个函数为常函数

常函数内不可以修改成员属性

成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

声明对象前加const称为常对象

常对象只能调用常函数

#include <iostream>using namespace std;
//常函数
class Person
{
public://this 指针的本质  是指针常量  指针的指向是不可以修改的//const Person *const this;//在成员函数后加const,修饰的是this指向,让指针指向的值也不可以修改void showPerson() const{this->m_B=100;// this->m_A=100;//this=NULL;//this指针不可以修改指针的指向+}void func(){m_A=100;}int m_A;mutable int m_B;//特殊变量,即使在常函数中,也可以修改这个值,加mutable
};void test01()
{Person p;p.showPerson();
}//常对象
void test02()
{const Person p;//在对象前加const,变为常对象//p.m_A=100;p.m_B=100;//m_B是特殊值,在常对象下也可以修改//常对象只能调用常函数p.showPerson();//correct//p.func();wrong 
}
int main()
{test01();system("pause");return 0;
}

若有侵权,请联系作者

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

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

相关文章

什么是机器视觉?

什么是机器视觉 近年来&#xff0c;人工智能渐渐成为一个热点话题。作为人工智能领域的一个分支&#xff0c;图像处理技术也随之发展到了一个新的高度&#xff0c;各种新的软件工具、算法库、开源资料不断涌现&#xff0c;各行各业也渐渐开始进行技术变革。比较典型的例子是&a…

【论文笔记】Gemma: Open Models Based on Gemini Research and Technology

Gemma 日期: March 5, 2024 平台: CSDN, 知乎 状态: Writing Gemma: Open Models Based on Gemini Research and Technology 谷歌最近放出的Gemma模型【模型名字来源于拉丁文gemma&#xff0c;意为宝石】采用的是与先前Gemini相同的架构。这次谷歌开源了两个规模的模型&…

【2024】vue-router和pinia的配置使用

目录 vue-routerpiniavue-routerpinia进阶用法---动态路由 有同学在项目初始化后没有下载vue-router和pinia&#xff0c;下面开始&#xff1a; vue-router npm install vue-router然后在src目录下创建文件夹router&#xff0c;以及下面的index.ts文件&#xff1a; 写进下面的…

短剧小程序:掌中剧院,随时上演

在繁忙的生活节奏中&#xff0c;我们总渴望找到一片属于自己的休闲空间。短剧小程序&#xff0c;就是这样一处随时随地都能让你沉浸其中的掌上剧院。无论你在何处&#xff0c;无论何时&#xff0c;只要轻轻一点&#xff0c;精彩纷呈的短剧即刻上演。 一、掌中剧院&#xff0c;…

学习Java的第二天

如何使用文本文档在cmd里打印出HelloWorld 1、创建一个文本文档&#xff0c;并命名为HelloWorld&#xff0c;将后缀改为java&#xff08;需要自己去把后缀打开显示出来&#xff09; 2、打开编辑 也可以双击打开 3、在里面写出以下代码 上面红框里为你要打印的语句&#xff0c;…

MySql、Navicat 软件安装 + Navicat简单操作(建数据库,表)

一、MySql、Navicat 软件安装 及正常使用 MySql下载&#xff0b;安装&#xff1a; 检查安装情况&#xff1a; 配置环境变量&#xff1a; 搞定了&#xff01;&#xff01;&#xff01; 可以登陆试哈哈哈 连接navicat 开始创建数据库 二、 商品种类表 - commoditytype int …

【C++】102.二叉树的层序遍历

题目描述 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]]示例 2&#xff1…

自学Java的第二十一天(在学校版)

一&#xff0c;每日收获 类与对象 1.看一个养猫猫问题 2.使用现有技术解决 Object01.java 3.类与对象的关系示意图 4.快速入门 5.类和对象的区别和联系 6.对象在内存中存在形式 7.属性/成员变量/字段 8.如何创建对象 9.如何访问属性 二&#xff0c;新名词与小技巧 三…

【Linux】Linux网络故障排查与解决指南

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 检查网络连接状态&#xff1a; 检查路由表&#xff1a; 检查DNS配置&#xff1a; 检查网络连接状态&#xff1a; 检查防火墙设…

Igraph入门指南 2

3、图的基本要素——边(Edge|Arc) 图的最本质的内容是一种二元关系&#xff0c;如果给这种二元关系赋予一个方向&#xff0c;就产生了有向图和无向图的分类&#xff0c;在教材中&#xff0c;无向的边叫Edge&#xff0c;有向的边叫Arc&#xff0c;另外&#xff0c;根据两个顶点…

KOA优化高斯回归预测(matlab代码)

KOA-高斯回归预测matlab代码 开普勒优化算法&#xff08;Kepler Optimization Algorithm&#xff0c;KOA&#xff09;是一种元启发式算法&#xff0c;灵感来源于开普勒的行星运动规律。该算法模拟行星在不同时间的位置和速度&#xff0c;每个行星代表一个候选解&#xff0c;在…

指针数组初始化,不常见啊

今天无意间看到这样一段代码&#xff0c;因为还是第一次看到&#xff0c;这是glibc库里的代码&#xff0c;写出来分享一下&#xff1a; #ifndef ERR_MAP # define ERR_MAP(n) n #endif const char *const _sys_errlist_internal[] { #define _S(n, str) [ERR_MAP(n)] …

WebGIS开发0基础必看教程:矢量查询

1.前言 在第七章里我们知道了WebGIS中要素的本质是UIComponent&#xff0c;而矢量图层的本质是包含了n&#xff08;n>0&#xff09;个UIComponent的Canvas。我们在UIComponent的graphics中&#xff0c;根据矢量数据画出矢量的形状(shape)&#xff0c;并且将矢量数据的属性(…

WordPress建站入门教程:小皮面板phpstudy如何安装PHP和切换php版本?

小皮面板phpstudy支持的PHP版本有很多&#xff0c;包括5.2.17、5.3.29、5.4.45、5.5.9、5.6.9、7.0.9、7.1.9、7.2.9、7.3.4、7.3.9、7.4.3、8.0.2、8.2.9。那么我们如何安装其他的php版本和切换网站的php版本呢&#xff1f;只需要简单几步即可&#xff0c;具体如下&#xff1a…

第十一篇 - 应用于市场营销视频场景中的人工智能和机器学习技术 – Video --- 我为什么要翻译介绍美国人工智能科技巨头IAB公司?

IAB平台&#xff0c;使命和功能 IAB成立于1996年&#xff0c;总部位于纽约市。 作为美国的人工智能科技巨头社会媒体和营销专业平台公司&#xff0c;互动广告局&#xff08;IAB- the Interactive Advertising Bureau&#xff09;自1996年成立以来&#xff0c;先后为700多家媒体…

【工作向】protobuf编译生成pb.cc和pb.py文件

序言 首先通过protoc --version查看protoc版本&#xff0c;避免pb文件生成方和使用方版本不一致 1. 生成pb.cc 生成命令 protoc -I${proto_file_dir} --cpp_out${pb_file_dir} *.proto参数&#xff1a; -I表示 proto 文件的路径&#xff1b; --cpp_out 表示输出路径&#xff…

被问爆了!多微信朋友圈一键转发大揭秘!

对于很多企业和个人而言&#xff0c;朋友圈是一个非常重要的营销渠道。不仅可以吸引潜在客户的关注&#xff0c;还能提高产品和服务的曝光率。 今天&#xff0c;就给大家分享一个能够多个微信号一键转发朋友圈的工具——微信管理系统&#xff0c;帮助大家提高工作效率&#xf…

网工内推 | 华为成都研究所,24届应届生人才储备计划

华为成都研究所 招聘岗位 网络工程师&#xff08;2024应届&#xff09; 岗位要求 24届的学员 本科公办院校 英语4/6级 有HCIP优先 工作地点 成都 私信小编&#xff0c;回复【内推】&#xff0c;获取内推名额申请资格~ 想获取更多『 思科 | 华为 | 红帽 认证真题 』、『 网…

jdk安装,配置path系统变量

直接点击安装 不要包含空格&#xff0c;中文字符 3.找到刚刚的路径&#xff0c;看一下&#xff0c;有东西就说明安装对了 配置path winr输入sysdm.cpl点击确定 全部依次点击 确定 即可。 验证jdk是否安装成功 看java、javac是否可用看java、javac版本号是否无问题 win…

FlyClient SPV client轻量化

这篇文章主要是为了构建一种轻客户端的算法。 如果使用SPV 的方式验证交易&#xff0c;每个client上面需要存储非常多的header。使用 proofs of proof-of-work 的方式&#xff0c;使得请客户端仅仅下载少量的区块头就能验证这一条链的安全性&#xff0c;然后再对包含交易的区块…