C++面向对象编程

news2024/11/14 20:38:12

面向对象编程

  • 面向对象编程和面向过程编程
    • 面向过程
    • 面向对象
  • 类型设计
  • 类的成员函数
  • 对象的创建和使用
    • C++对象模型
    • this指针
    • 构造函数和析构函数
      • 构造函数定义和使用
      • 析构函数的定义和使用
    • 对象的生存周期
    • 拷贝构造函数
      • 深拷贝与浅拷贝
    • 运算符的重载

面向对象编程和面向过程编程

面向过程

我们学过C语言,其就是一种面向过程的编程流程,以事件为中心,解决事件时分析出流程,然后用函数实现一系列流程,然后按照流程一步一步按顺序调用函数。
优点:流程化使得编程任务明确,在开发之前基本考虑了实现方式和最终结果,具体步骤清楚,便于节点分析。
效率高,面向过程强调代码的短小精悍,善于结合数据结构来开发高效率的程序。
缺点:需要深入的思考,耗费精力,代码重用性低,扩展能力差,后期维护难度比较大。

面向对象

在编程时,一些简单问题可以通过面向过程来解决,但是当问题规模变大的时候就有困难了。编程本质来源于现实生活,我们的生活中可以看成一个一个对象,每个对象拥有一定的属性,这就引入了面向对象编程的概念,面向对象是一种以“对象”为中心的编程思想,把要解决的问题分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个对象在整个解决问题的步骤中的属性和行为。
优点:
结构清晰,程序是模块化和结构化,更加符合人类的思维方式;
缺点:
开销大,当要修改对象内部时,对象的属性不允许外部直接存取,所以要增加许多没有其他意义、只负责读或写的行为。这会为编程工作增加负担,增加运行开销。

类型设计

封装是C++中最基本的一个属性,但是封装从何而来呢?C++将对象的属性和操作都放入一个类里,这就是其面向对象的本质。
模板:

class 类型名称 {
public:成员列表;
private:成员列表;
protected:成员列表;
};

以上便是类的模板,模板中给了三个不同的数据类型,public是公有成员,可以从外部访问(例:此处的成员可以直接在主函数中调用),而private(私有的),protected(保护的)中的成员不能直接调用,只能通过公有函数进行访问改变其属性,这就体现了C++的封装特性。

类的成员函数

类是一种数据类型,定义时系统不为其分配存储空间,所以不能对类的数据从成员进行初始化。也不能使用extern,auto,register关键字限定其存储类型。
成员函数可以直接使用类定义中的任意成员,可以处理数据成员,也可以调用函数成员,成员函数的定义:
类中一般只对成员函数进行声明,函数定义通常在类的说明之后进行,格式:

返回值类型 类名::函数名(参数表) {}
//我们可以举例如下:
class mybook {
public:
	void my_printf(char* name,int priace,int math,int nums);
private:
	char name[10];//书名
	int priace;//单价
	int math;//数量
	int nums;//总价
};
void mybook::my_printf(char* name,int priace,int math,int nums) {
	cout<<"name"<<name<<"price"<<price<<"math"<<math<<"nuns"<<nums<<endl;
}

对象的创建和使用

类只是对象的模板,就像盖房子图纸,有了类不一定存在对象,只是告诉编译器可以通过类可以定义对象(盖房子),为在内存中开辟出该对象的实例,
定义对象时我们可以直接通过类名来定义一个对象。

int a;
mybook java;

我们通过mybook类型名创建了一个对象-》java,同时为他分配了一定的存储空间来存放其数据和操作函数(代码)。
对象的使用很简单,就是在对象名后加上’.',紧接着加上成员函数名,和调用函数一样加上实参即可,但是此处必须是公有成员函数,私有和受保护的只能在成员函数中被调用,不能在外部直接调用。

C++对象模型

当定义对象数量为4时,我们的对象属性很显然会在数据区申请到4份存放数据(对象属性)的区域,而不同之处在于其操作属性的行为,也就是函数,成员属性我们能理解,每个对象是独立的,成员属性也应该是独立的,但是成员函数是操作对象属性的行为,然后对象属性相同,因此如果在代码区存放4份成员函数就会显得浪费内存,所以此处有两种形式,一是对象拥有独立的代码区存放成员函数,二是对象公用一份成员函数的代码,放在公共代码区。

this指针

class Int
{
 int value;
public:
 Int(int x = 0) :value(x) { cout << "Create Int Object " << this << endl; }
 Int(const Int& it) :value(it.value) { cout << "Copy Create Int Object" << this << endl; }
 Int& operator=(const Int& it)
 {
  if (this != &it)
  {
   value = it.value;
  }
  return *this;
 }
 ~Int() { cout << "Destroy Int " << this << endl; }

 void PintInt() const { cout << value << endl;  }
};
int main()
{
 Int a(10), b(20), c;
 Int d(b);
 c=a;
 c.PintInt();

 return 0;
}

编译器对程序员自己设计的类型分三次编译:
第一:识别记录类体种的属性名称,类型和访问权限,与属性在类体中的位置无关。
第二:识别和记录类体中函数原型(返回类型+函数名+参数表),形参的默认值,访问限定,不识别函数体。
第三:改写在类中定义函数的参数列表,改写对象调用成员函数的形式。
在成员函数中一般都会存在this指针,例如构造函数,拷贝构造函数等
如上代码:我们以运算符重载函数为例,c=a <=>c.operator=(a)<=>operator(&c,a);我们可以看出c对象在调用运算符=重载函数时,此处this指针指向对象c,一般函数会省略掉this指针,我们可以还原函数编译阶段的参数,

Int& operator=(const Int& it)
//Int& operator(Int *const this,cosnt Int & it );而this指针便指向我们调用函数的对象

this指针特点:

  • 只能在成员函数中使用,在全局函数,静态成员函数中都不能使用this指针。this指针始终指向当前对象。
  • this指针是在成员函数的开始前构造,并且在成员函数结束后清楚。
  • this指针会因编译器不同而有不同的存储位置,可能是栈,寄存器或全局变量。

构造函数和析构函数

构造函数定义和使用

类的数据成员多为私有的,不能从外部进行操作,因此需要通过公有的成员函数进行操作赋值,而构造函数便是再创建对象的同时进行初始化的函数,注意只能初始化一次。
作用:

  • 创建对象
  • 初始化对象
  • 类型转换

构造函数的特点:

  • 函数名同类型名
  • 无函数返回类型,此处并非void,即什么也不写。其实构造函数本质上存在返回类型,同类名,返回的就是我们创建的对象。
  • 程序运行时,当对象被创建时自动调用构造函数,并且只能调用一次,生存期也就仅仅这一次调用。
  • 构造函数可以进行重载,其实可以构造多个构造函数,根据参数不同在创建对象的同时选择一个构造函数进行创建并初始化。
  • 构造函数可以在类内定义,也可以在类内声明类外定义。
  • 如果没有给出构造函数,那么编译器会默认给出一个缺省构造函数。但是如果给出了缺省构造函数那么编译器便不会给出构造函数。
class Int {
private: int value;
public:
	Int(int c=0) {
		this->value=c;
	}
};

析构函数的定义和使用

当创建对象时,系统会自动调用构造函数从而进行初始化,当然当对象生命周期结束的时候页会自动调用析构函数,对对象进行注销并且做一系列善后工作,这就是析构函数。

  • 析构函数名同类名,只是在类名前加上‘~’
  • 析构函数没有给出的话,系统会自动生成析构函数。
  • 析构函数没有返回值和无类型。
  • 一个类只有一个析构函数。
class mystring {
private: char* ptr;
public:
 	mystring(const char* s=nullptr) {
		if(this!=nullptr) {
			int len=strlen(s)+1;
			ptr=new char[len];
			strcpy_s(ptr,len,s);
		}else {
			ptr=new char('\0');
		}
	}
	~mystring() {
		delete[]ptr;
		ptr=nullptr;
	}
};

构造函数和析构函数的区别是:1、构造函数可以接收参数,能够在创建对象时赋值给对象属性,析构函数不能带参数;2、创建对象时调用构造函数,析构函数是在销毁对象时自动调用的。

对象的生存周期

  • 局部对象:对于局部定义的对象,当程序抵达对象定义时调用构造函数,程序运行走出局部域的时候会自动调用析构函数。
  • 静态局部对象:程序运行首次到达对象定义时调用构造函数,整个程序结束时调用析构函数
  • 全局对象:程序进入main函数之前就已经定义了对象,这时要调用构造函数,整个程序结束时调用析构函数。
  • 动态创建对象:使用new创建对象,使用delete释放对象。
class mystring {
private: char* ptr;
public:
 	mystring(const char* s=nullptr) {
		if(this!=nullptr) {
			int len=strlen(s)+1;
			ptr=new char[len];
			strcpy_s(ptr,len,s);
		}else {
			ptr=new char('\0');
		}
	}
	~mystring() {
		delete[]ptr;
		ptr=nullptr;
	}
};
void fun() {
	mystring d("ace");//局部对象
	static mystring e("afa");//静态局部对象
}
mystring a;//全局对象
int main() {
	mystring b("hello");
	mystring *c=new mystring{"xxin"};//动态创建对象
	delete c;
	mystring q;
	new(q) mystring("xxin");//定位new
	fun(); 
}

拷贝构造函数

拷贝构造就是用一个对象的数据拷贝一份初始化另一个对象,这个过程需要拷贝数据成员。在建立对象的时候用一个已存在的对象来初始化该对象的内存空间,这就是拷贝构造函数。

mystring(const mystring& it) {
	int len=strlen(it.ptr);
	ptr=new char[len];
	strcpy_s(ptr,len,it.ptr);
}
  • 拷贝构造函数是构造函数的一个重载形式。
  • 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值回进入无穷递归调用。
  • 若显示定义,系统自动生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,称为:位拷贝。

深拷贝与浅拷贝

我们知道在创建字符串这个类的时候,字符串存在于堆中,而对象中只存在一个指向堆区字符串的指针,正所谓浅拷贝就是在创建这个对象时,仅仅将这个对象的指针指向待拷贝对象的堆区字符串,两者公用一块堆区空间,记住此时只能释放一次,否则程序会崩掉。而深拷贝就是将堆区资源也进行了一份拷贝,在堆区重新申请了一块空间,待拷贝字符串复制一份存放在这块空间中,对象的指针指向这块新的堆区空间。大部分拷贝都是深拷贝,大家也可以用浅拷贝仿写一下mystring类并实现代码。

运算符的重载

运算符重载我觉得本质上是一种特殊的函数重载,一般为类的成员函数,

  • 运算符重载的函数名必须为关键字operator加上一个合法的运算符。在调用该函数时,将右操作数作为函数的实参。
  • 当用类的成员函数实现运算符重载时,运算符重载函数的参数为一个或者没有,运算符的左操作数一定是对象,因为重载的运算符是该对象的成员函数,而右操作数是该函数的参数。
  • 单目运算符中前置++和后置++的问题
    前置++:返回类型 类名::operator++() {}
    后置++:返回类型 类名::operator++(int){}
    后置++中的参数int用于区别是前置++还是后置++,并无实际意义,可以给一个变量名,也可以不给。大家发现很多经验丰富的程序员平时编写程序时会使用前置++,很少使用后置++,这是什么呢?
    比如我们写一个for循环程序,而循环中++是对自己创建的一个类对象++,在运行时我们就会发现使用后置++会导致每一次自增都会生成一个新的对象,而前置++并不会,所以一般会使用前置++
  • C++中只有极少数的运算符不允许重载:在这里插入图片描述
    还有#,##,//,/*, */

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

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

相关文章

设计模式之【适配器模式】,两个人之间确实需要月老的搭线~

文章目录 一、什么是适配器模式1、适配器模式使用场景2、代理、桥接、装饰器、适配器 4 种设计模式的区别3、适配器模式结构 二、类适配器1、实例 三、对象适配器1、实例 四、接口适配器1、实例 五、源码中的应用 一、什么是适配器模式 适配器模式&#xff08;Adapter Design …

第2天学习Docker——Docker安装

一、前言 Docker 是一个用于开发、传送和运行应用程序的开放平台。Docker 使您能够将应用程序与基础设施分开&#xff0c;以便您可以快速交付软件。使用 Docker&#xff0c;您可以像管理应用程序一样管理基础设施。通过利用 Docker 的快速交付、测试和部署代码的方法&#xff0…

vue3学习一 setup

vue3中没有 data 等配置项&#xff0c; 并且它的引入方式也不是像vue2中的 import vue就可以了&#xff0c; 而是用到什么再引入什么&#xff0c; 其中 setup() 函数&#xff0c; 是最大的区别 vue3中的 setup 有点像vue2中的 data , 但又不完全是 setup 会在 生命周期create…

关于C语言

C99是啥 很多书籍开篇会突然提到C99标准&#xff0c;因此这里搜了一下。 C99是C语言的官方标准第二版。1999年12月1日&#xff0c;国际标准化组织和国际电工委员会旗下的C语言标准委员会正式发布了这个标准文件 &#xff1b; C99是在C89和C90的基础上发展起来的&#xff0c;增加…

独立站平台选哪个好?5个独立站平台优缺点分析

选择适合自己的独立站平台需要综合考虑多个方面的因素&#xff0c;包括平台的优缺点、自己的需求和预算等因素。下面是几个常见的独立站平台的优缺点分析供您参考&#xff1a; 一、Shopify&#xff1a; 优点&#xff1a;简单易用&#xff0c;拥有丰富的主题和应用程序&#xf…

JavaScript实现求1-100之间不能被3整除的数之和,求100以内偶数的和的两个程序代码

以下为实现求1-100之间不能被3整除数之和求100以内偶数的和的两个程序代码和运行截图 目录 前言 一、实现输入两个数比较两个数的大小 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 二、求100以内偶数的和 2.1 运行流程及思想 2.2 代码段 2.3…

泰坦尼克号幸存者预测(案例)----决策树版

1、导入需要的库 import pandas as pd from sklearn.tree import DecisionTreeClassifier import matplotlib.pyplot as plt from sklearn.model_selection import GridSearchCV2、导入数据 在此下载泰坦尼克号训练数据 data pd.read_csv(r"F:\data\train1.csv") …

内网安全:Cobalt Strike 安装.

内网安全&#xff1a;Cobalt Strike 安装. Cobalt Strike是一款渗透测试神器&#xff08;又称为CS&#xff09;。拥有多种协议主机上线方式&#xff0c;集成了端口转发&#xff0c;socket代理&#xff0c;office攻击&#xff0c;文件捆绑&#xff0c;钓鱼&#xff0c;提权&…

哈希算法原理与应用:确保数据完整性和安全性的关键技术

哈希算法是一种将任意长度的消息映射为固定长度摘要或哈希值的算法。哈希算法主要被用于验证数据的完整性和安全性。哈希算法的应用广泛&#xff0c;例如在密码学中用于验证数据完整性和数字签名&#xff0c;也用于检索数据和进行散列分布式存储。下面将详细介绍哈希算法的原理…

《Linux 内核设计与实现》12. 内存管理

文章目录 页区获得页获得填充为 0 的页释放页 kmalloc()gfp_mask 标志kfree()vmalloc() slab 层slab 层的设计slab 分配器的接口 在栈上的静态分配单页内核栈 高端内存的映射永久映射临时映射 每个 CPU 的分配新的每个 CPU 接口 页 struct page 结构表示系统中的物理页&#x…

区间预测 | MATLAB实现QRCNN卷积神经网络分位数回归时间序列区间预测

区间预测 | MATLAB实现QRCNN卷积神经网络分位数回归时间序列区间预测 目录 区间预测 | MATLAB实现QRCNN卷积神经网络分位数回归时间序列区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 区间预测 | MATLAB实现QRCNN卷积神经网络分位数回归时间序列区间预测…

超级详细的 FinalShell 安装 及使用教程

一、引言 FinalShell 是一款免费的国产的集 SSH 工具、服务器管理、远程桌面加速的良心软件&#xff0c;同时支持 Windows,macOS,Linux&#xff0c;它不单单是一个 SSH 工具&#xff0c;完整的说法应该叫一体化的的服务器&#xff0c;网络管理软件&#xff0c;在很大程度上可以…

新唐NUC980使用记录(5.10.y内核):在用户应用中使用GPIO

文章目录 目的使用参考与演示使用参考存在的问题问题定位修改设备树使用测试 总结设备树文件内容 目的 GPIO是最基础的外设&#xff0c;使用频率也非常高&#xff0c;这篇文章将简单体验在NUC980 Liunx用户应用中使用GPIO功能。 这篇文章中内容均在下面的开发板上进行测试&am…

【java EE】Redis基础

Redis基础 业务中会遇到的问题&#xff1a; 数据量巨大数据模式的不确定性数据的频繁读数据的频繁更改大量数据的统计分析 集中数据库的特点 Redis简介&#xff1a; Redis&#xff08;Remote Dictionary Server&#xff09;是一个使用ANSI C语言编写的开源数据库高性能的 …

解密Web自动化测试:你需要了解的四大难点

B站首推&#xff01;2023最详细自动化测试合集&#xff0c;小白皆可掌握&#xff0c;让测试变得简单、快捷、可靠https://www.bilibili.com/video/BV1ua4y1V7Db 目录 前言 问题1&#xff1a;测试稳定性 问题2&#xff1a;测试可维护性 问题3&#xff1a;测试性能 问题4&am…

阿里熔断限流框架Sentinel实现流程和动态规则数据源

文章目录 1.简单介绍2.使用示例3.主要实现原理和组成部分4.动态规则数据源 本篇文章主要介绍熔断限流框架Sentinel的使用示例、组成原理和动态规则数据源的实现原理。 1.简单介绍 阿里的熔断限流框架Sentinel基于滑动时间窗口实现熔断限流管控的&#xff0c;支持多样的管控场景…

PHP 基础入门

目录 1、标记 2、注释 3、输出语句 4、关键字 5、常量的定义与使用 6、预定义常量 7、变量的赋值&#xff08;传参赋值与引用赋值&#xff09; 8、可变变量 9、双引号和单引号的区别 10、heredoc结构和nowdoc结构 11、其他符号 1、标记 <?php 和 ?> 是PHP标…

【MySQL】EXPLAIN 语句 各字段 详解

EXPLAIN 语句 概貌 在连接查询的执行计划中&#xff1a; 每个表都会对应一条记录&#xff0c;这些记录的 id 列的值是相同的&#xff1b; 在包含子查询的执行计划中 &#xff1a;每个 select关键字都会对应一个唯一的 id 值。 驱动表&#xff1a;出现在前面的表&#xff1b; …

Apache ECharts 一个基于 JavaScript 的开源可视化图表库

一&#xff1a; ECharts 特性 ECharts&#xff0c;一个使用 JavaScript 实现的开源可视化库&#xff0c;可以流畅的运行在 PC 和移动设备上&#xff0c;兼容当前绝大部分浏览器&#xff08;IE9/10/11&#xff0c;Chrome&#xff0c;Firefox&#xff0c;Safari等&#xff09;&a…

谷歌正在向所有账户推出密码终止技术

谷歌宣布让其个人帐户持有人使用称为“密码”的密码替代登录的一项重大努力。 该功能面向公司的数十亿帐户推出&#xff0c;用户将能够主动寻找并启用它。谷歌表示&#xff0c;它计划在未来几个月推广密码&#xff0c;并开始推动账户持有人将他们传统的用户名和密码登录转换为…