2,继承、内联函数、虚继承、友元、构造析构函数、初始化列表

news2024/9/27 21:28:07

继承

  • 2.1结构体成员权限
    • 2.1.1访问权限
    • 2.1.2类与结构体
  • 2.2类的成员函数
    • 2.2.1类内规则
    • 2.2.2类成员内联函数inline
  • 2.3类的继承
    • 2.3.1类的继承与成员函数
    • 2.3.2类的多继承
      • 2.3.2.1类的多继承:菱形问题提出
    • 2.3.3类的虚继承(关键字virtual)
  • 2.4友元
    • 2.4.1友元类
    • 2.4.2友元函数
  • 2.5构造函数和析构函数
  • 2.6初始化列表

2.1结构体成员权限

2.1.1访问权限

访问权限有三种:
public 公共权限(类内可以访问,类外可以访问)

protected 保护权限(类内可以访问,类外不可以访问)(子类可以访问父类中的保护内容)

private 私有权限(类内可以访问,类外不可以访问)(子类不可以访问父类中的私有内容)

注意:protected 和 private 的设计是为了继承

2.1.2类与结构体

类:
类在游戏开发中用的最多
如果没有在类中显式定义构造函数和析构函数并指定访问权限,则默认情况下它们的权限都是公有的(public)
结构体:
结构体一般用于储存变量或数据
结构体也有构造(初始化)和析构(释放内存等),不写编译器就会默认生成,在游戏中对对象的生命周期的操作

#include<iostream>
using namespace std;
struct Test01
{
public:
	int a;
	void test01() {}
protected:
	int b;
	void test02() {}
private:
	float c;
	void test03() {}
};
class Test02
{
public:
	int d;
	void test04() {}
protected:
	int e;
	void test05() {}
private:
	float f;
	void test06() {}
};
int main()
{
	Test01 A;
	A.a = 10;
	int num1 = A.a;
	A.test01();
	//A.c;//因为设置了保护权限,所以不可以访问
	//A.test03();//因为设置了私有权限,所以不可以访问

	Test02 B;
	B.d = 20;
	int num2 = B.d;
	B.test04();
	//B.e;//因为设置了保护权限,所以不可以访问
	//B.test06();//因为设置了私有权限,所以不可以访问
	system("pause");
	return 0;
}

2.2类的成员函数

2.2.1类内规则

类内可以写静态函数、成员函数
静态函数不可以调用成员函数,成员函数可以调用静态函数
静态函数内不可以包含类内的成员信息(如果想调用需要使成员信息也变为静态)
全局的调用方式::

#include<iostream>
using namespace std;
int Fun1()
{
	return 1;
}

class Test01
{
public:
	Test01();
	~Test01();
	static int a;
	static int b;
	static int Fun1()//类内可以添加静态函数
	{
		return a+b; //无法直接调用int a;和int b;需要转换为static形式
	}
	int Fun2()//类内可以添加成员函数
	{
		Test01::Fun1();//成员函数可以调用静态函数
		return 3;
	}
};
int Test01::a = 0;
int Test01::b = 0;
Test01::Test01()
{
	int num1 = ::Fun1();//::调用全局范围中的
	cout << num1 << endl;
}
Test01::~Test01()
{

}

struct Test02
{
public:
	static void Fun1();
};

void Test02::Fun1() {}
int main()
{
	Test01 A;//实例化
	Test01::Fun1();
	A.Fun1();
	A.Fun2();

	Test02::Fun1();
	system("pause");
	return 0;
}

2.2.2类成员内联函数inline

为什么inline的效率会高效,因为编译时编译器会把代码副本放在每个调用函数的地方

可以使用在类成员内,也可以使用在全局中,也可以使用在结构体内

将简短的、频繁调用的函数声明为内联函数可以获得较好的效果。但是,对于复杂的函数或者在循环中调用的函数,使用内联可能导致代码膨胀,反而会影响性能如for、switch、递归等。

ue4中的FORCEINLINE也代表着内敛的效果,使用FORCEINLINE使需要包含头文件“CoreMinimal.h”

#include<iostream>
using namespace std;
class Test01
{
public:
	//想要获取私有成员,不想私有成员被赋值可以用inline
	//比下面注释中的代码高效很多
	inline int Geta()const
	{
		return a;
	}
	/*int Geta()const
	{
		return a;
	}*/
private:
	int a;
};

inline int Getb()
{
	return 0;
}

struct Test02
{
public:
	inline int Getc()const
	{
		return c;
	}
private:
	int c;
};

int main()
{
	Test01 A;
	A.Geta();

	Getb();

	Test02 C;
	C.Getc();

	system("pause");
	return 0;
}

2.3类的继承

2.3.1类的继承与成员函数

父类也成为基类,子类也称为派生类
封装SDK(插件)时只需要封装基类,其他人使用时只需要继承即可使用其中的方法,或者去扩展
游戏开发时大部分用的都是公共继承

//public
//   public      public
//   protected   protected
//   private     private
//provected
//   public      protected
//   protected   protected
//   private     private
//private
//   public      private
//   protected   private
//   private     private

2.3.2类的多继承

使用场景:如下列代码,将每一个系统部分封装到各个模块中,需要使用时直接调用即可,非常方便
可以将对象转换成某个模块
不需要直接转换成类,可以直接调接口,三个指针指向不同的继承接口,所以可以直接通过指针
请添加图片描述

#include<iostream>
using namespace std;
class UObject
{

};

class AActor :public UObject//只管和对象有关的
{
public:
	void Start() {}
	void End() {}
	void Net() {}
};

class IPhysics//只管物理
{
public:
	void Simulate() {}
};

class IAttack//攻击接口
{
public:
	void AttackTarget(ACharacter* InTarget) {}
};

class ACharacter :public AActor,public IPhysics,public IAttack
{

};

bool IsSimulate(IPhysics* p)//好处是不需要直接转换成类,可以直接调接口
{
	if (p)
	{
		p->Simulate();
	}
	return true;
}
int main()
{
	ACharacter A;//多继承
	ACharacter B;
	A.Start();
	A.End();
	A.Simulate();
	A.AttackTarget(&B);

	//A为多继承的,继承了IPhysics、IAttack、AActor
	IPhysics* p = &A;
//因为A继承了IPhysics接口(通过ACharacter类的多继承),
//A对象可以被视为一个IPhysics对象
//将A对象的地址赋值给指针p,就可以通过指针p来访问A对象的IPhysics接口
	IAttack* p1 = &A;
	AActor* p2 = &A;

	IsSimulate(&A);

	system("pause");
	return 0;
}

2.3.2.1类的多继承:菱形问题提出

A的类型为ACharacter,同时继承了AActor和UHello,而AActor和UHello同时继承了UObject,用A调用UObject时会出现二义性,不知道调用的是AActor的UObject还是UHello的UObject
A.Destroy();调用的是UHello的还是AActor的?
这既是菱形问题,要避免
解决菱形问题需要用到虚函数,在下文会解决

#include<iostream>
using namespace std;
class UObject
{
public:
	void Destroy() {}
};

class AActor :public UObject//只管和对象有关的
{
public:
	void Start() {}
	void End() {}
	void Net() {}
};

class UHello :public UObject
{

};

class IPhysics//只管物理
{
public:
	void Simulate() {}
};

class ACharacter;//因为class ACharacter在后面,所以先声明,也可以在IAttak内部使用前加class
class IAttack//攻击接口
{
public:
	void AttackTarget(class ACharacter* InTarget) {}
};

class ACharacter :public AActor,public IPhysics,public IAttack,public UHello
{

};

bool IsSimulate(IPhysics* p)//好处是不需要直接转换成类,可以直接调接口
{
	if (p)
	{
		p->Simulate();
	}
	return true;
}
int main()
{
	ACharacter A;
	IPhysics* p = &A;
	IAttack* p1 = &A;
	AActor* p2 = &A;

	IsSimulate(&A);

	//A.Destroy();//会报错,类型不明确
	//调用的是UHello的还是AActor的
	system("pause");
	return 0;
}

2.3.3类的虚继承(关键字virtual)

虚继承使class B和class C继承的public A变成一个共享的类,因此class D只用调用一次A
尽量避免虚继承问题

#include<iostream>
using namespace std;
class A
{
public:
	A()
	{
		printf("A\n");
	}
	void Hello()
	{
		printf("Hello\n");
	}
};
class B :virtual public A
{
public:
	B() :A() {}
};
class C :virtual public A
{
public:
	C() :A() {}
};
class D :public B, public C
{
public:
	D()//c保错,编译器不知道构造B还是C
	{

	}
	~D() 
	{

	}
};
int main()
{
	D d;
	d;
	//没虚继承前输出结果为A输出了两次
	//A
	//A
	d.Hello();
	//正常情况下会报错,在class B和C的继承public A前加上virtual变为虚继承
	//输出结果为
	//A
	//Hello
	system("pause");
	return 0;
}

2.4友元

2.4.1友元类

使用友元可以访问另一个类内的所有内容

#include<iostream>
using namespace std;
class FTestClass
{
	friend class FTest2;//友元
public:
	void Hello() {}
private:
	void Hello1() {}
	void Hello2() {}
	void Hello3() {}
protected:
	void Hello4() {}
};

class FTest2
{
public:
	void Init()
	{
		Class.Hello();
		Class.Hello1();//因为是private权限,正常会报错,使用友元不会报错
		Class.Hello4();//因为是protected权限,正常会报错,使用友元不会报错
	}
private:
	FTestClass Class;
};

int main()
{
	FTest2 Test2;
	Test2.Init();

	system("pause");
	return 0;
}

2.4.2友元函数

友元不能被继承
优点:提高了程序的运行效率
缺点:破坏了类的封装以及稳点性
友元关系本身不具备继承性

#include<iostream>
using namespace std;
class FTest1
{
public:
	friend void Printf_f(FTest1 &T)
	{
		T.Hello();
		printf("%d\n", T.a);
		printf("%d\n", T.b);
	}

	void Printf_a()
	{
		printf("%d\n", a);
		printf("%d\n", b);
		printf("%d\n", c);
	}
	int c = 100;

	static void Printf_b(FTest1& T)
	{
		T.Hello();
		printf("%d\n", T.a);
		printf("%d\n", T.b);
	}
private:
	void Hello()
	{
		a = 0;//其实是this->a=0;编译器省略了
		b = 10;
	}
private:
	int a;
	int b;
};

int main()
{
	FTest1 A;
	Printf_f(A);

	A.Printf_a();

	FTest1::Printf_b(A);//静态函数

	system("pause");
	return 0;
}

2.5构造函数和析构函数

#include<iostream>
using namespace std;
class FTest1
{
public:

};

class FTestA
{
public:
	FTestA();
	FTestA(int ina, int inb, int inc);
	~FTestA();
public:
	int a;
	int b;
	int c;
	FTest1* T;
};

FTestA::FTestA()
{
	a = 1;
	b = 2;
	c = 3;
}

FTestA::FTestA(int ina, int inb, int inc)
{
	a = ina;
	b = inb;
	c = inc;

	T = new FTest1();//分配内存
}

FTestA::~FTestA()
{
	if (T)
	{
		delete T;
		T = nullptr;//将指针类型的变量 T 初始化为空指针
	}
}

int main()
{
	FTestA A;//会走FTestA::FTestA()
	cout << A.a << " " << A.b << " " << A.c << endl;
	FTestA B(10,20,30);//会走FTestA(int ina, int inb, int inc);
	cout << B.a << " " << B.b << " " << B.c << endl;

	system("pause");
	return 0;
}

2.6初始化列表

初始化列表的语法是在构造函数的参数列表后使用冒号 “:”,接着是多个初始化器,每个初始化器由成员变量名和对应的初始值用逗号分隔。
有父类时父类排在最前面
一定要按照变量声明的顺序排列

初始化列表的优点:
初始化非静态常量成员变量:对于非静态常量成员变量,它们只能在初始化列表中进行赋值,不能在构造函数中赋值。
初始化引用成员变量:引用成员变量必须在构造函数中进行初始化,并且只能使用初始化列表进行初始化。
初始化基类成员变量:如果派生类包含一个基类,那么初始化列表可以用于调用基类的构造函数,并初始化基类成员变量。
性能优化:使用初始化列表可以避免先创建再赋值的过程,提高效率。

注意:
初始化列表只能在构造函数中使用
初始化顺序固定
初始化列表只能在编译时确定初始值,无法在运行时根据条件或变量的值来决定初始值。
初始化列表中的初始化操作无法直接处理异常或错误。

#include<iostream>
using namespace std;
class FTest1
{
public:

};

class FHello_F{};

class FTestA:public FHello_F
{
public:
	FTestA();
	FTestA(int ina, int inb, int inc);
	~FTestA();
public:
	int a;
	int b;
	int c;
	FTest1* T;
};

FTestA::FTestA()
{
	a = 1;
	b = 2;
	c = 3;
}

FTestA::FTestA(int ina, int inb, int inc)
//初始化列表
	:FHello_F()//有父类时放在最前面
	,a(ina)//一定要按照变量声明的顺序排列
	,b(inb)
	,c(inc)
{
	T = new FTest1();//分配内存
}

FTestA::~FTestA()
{
	if (T)
	{
		delete T;
		T = nullptr;
	}
}

int main()
{
	FTestA A;//会走FTestA::FTestA()
	cout << A.a << " " << A.b << " " << A.c << endl;
	FTestA B(10,20,30);//会走FTestA(int ina, int inb, int inc);
	cout << B.a << " " << B.b << " " << B.c << endl;

	system("pause");
	return 0;
}

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

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

相关文章

Zookeeper入门介绍

Zookeeper在我本次系统的学习之前是已经开始使用了&#xff0c;但是并不理解Zookeeper到底是什么&#xff0c;有什么作用&#xff0c;你或许跟我有一样的疑惑&#xff0c;本专栏将会解决这些疑惑。 目录 Zookeeper介绍&#xff1a; zookeeper特点&#xff1a; 数据结构&#x…

Python 进阶(四):日期和时间(time、datetime、calendar 模块)

❤️ 博客主页&#xff1a;水滴技术 &#x1f338; 订阅专栏&#xff1a;Python 入门核心技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; 文章目录 1. time模块1.1 获取当前时间1.2 时间休眠1.3 格式化时间 2. datetime模块2.1 获取当前…

【Docker】Docker应用部署之Docekr容器安装Nginx

目录 一、搜索镜像 二、拉取镜像 三、创建容器 四、测试使用 一、搜索镜像 docker search nginx 二、拉取镜像 docker pull nginx # 不加冒号版本号 默认拉取最新版 三、创建容器 首先我们需要在宿主机创建数据卷目录 mkdir nginx # 创建目录 cd nginx # 进入目录 mkd…

【SAP Abap】记录一次SAP长文本内容通过Web页面完整显示的应用

【SAP Abap】记录一次SAP长文本内容通过Web页面完整显示的应用 1、业务背景2、实现效果3、开发代码3.1、拼接html3.2、显示html3.3、ALV导出Excel 1、业务背景 业务在销售订单中&#xff0c;通过长文本描述&#xff0c;记录了一些生产备注信息&#xff0c;如生产标准、客户要求…

【Java面试丨企业场景】常见技术场景

一、单点登录怎么实现的 1. 介绍 单点登录&#xff08;Single Sign On&#xff0c;SSO&#xff09;&#xff1a;只需要登录一次&#xff0c;就可以访问所有信任的应用系统 2. 解决方案 JWT解决单点登录问题 用户访问应用系统&#xff0c;会在网关判断Token是否有效如果Tok…

位运算 剑指offer15 二进制中1的个数 搜索算法:55-II 平衡二叉树 数值的整数次方 39数组中出现次数超过一半的数字

可能会引起死循环的解法&#xff1a; 看最右边一位是不是1&#xff0c;然后将输入的整数右移一位&#xff0c;再判断最右边一位&#xff08;即倒数第二位&#xff09;是否为1&#xff0c;接着再右移&#xff0c;知道整数移动到0为止 这个解法&#xff0c;把整数右移一位和把整数…

mfc140u.dll丢失怎样修复?这三个方法的可以修复

最近遇到了mfc140u.dll丢失的问题&#xff0c;让我感到非常困扰。在使用某个软件时&#xff0c;突然弹出了一个错误提示&#xff0c;说是mfc140u.dll文件不存在&#xff0c;导致该软件无法正常运行。一开始我并不知道这个文件是什么&#xff0c;也不知道为什么会丢失。于是我开…

7.29训练总结

CodeForces - 1609E 这种使得整个串不包含子串’abc’的题目&#xff0c;发现可以用线段树维护 #include<bits/stdc.h> using namespace std; const int maxn1e55; #define lson now<<1 #define rson now<<1|1 struct seg {int a,b,c;int ab,bc,abc; }tr[m…

使用开源免费AI绘图工具神器-Stable Diffusion懒人整合包

使用开源免费AI绘图工具神器-Stable Diffusion懒人整合包 Stable Diffusion 是什么 Stable Diffusion (简称 SD) 是一款开源免费的以文生图的 AI 扩散模型&#xff0c;它和付费的 Midjourney 被人称为当下最好用的 AI 绘画工具。你在网上看到的绝大多数优秀 AI 图片作品&…

智能垃圾桶

1.树莓派3B引脚图 2. 原理图 3.舵机线图 搜了这个这么多3b的资料&#xff0c;自己只是想解决如何下程序和运行程序的博客&#xff0c;网上搜集的资料全是讲如何通过SSH或者网线连接树莓派&#xff0c;通过直接连接屏幕的教程较少。 棕 : GND 红 : VCC 黄&#xff1a; 信号线…

【Ubuntu系统18.04虚拟机ros下实现darknet_ros(YOLO V3)检测问题解析最全】

原本打算在搭载Ubuntu18.04的智能小车上面运行使用darknet_ros 包来进行yolov3的检测&#xff0c;但是运行过程中遇到了不少问题&#xff0c;从头到尾部的运行包括遇到的解决方法以及对应的文章一并列出&#xff0c;免得到处查找。 首先是在ROS下实现darknet_ros(YOLO V3)检测…

127.【SpringBoot 源码刨析D】

SpringBoot 源码刨析D (三)、SpringBoot 核心功能4.单元测试功能(1).JUnit5 的变化(2).JUnit5常用注解(3).断言&#xff08; assertions &#xff09;(3.1).简单断言(3.2).数组断言(3.3).组合断言(3.4).异常断言(3.5).超时断言(3.6).快速失败 (4).前置条件&#xff08; assumpt…

【小白必看】Python爬虫实战之批量下载女神图片并保存到本地

文章目录 前言运行结果部分图片1. 引入所需库2. 发送请求获取网页内容3. 解析网页内容并提取图片地址和名称4. 下载并保存图片完整代码关键代码讲解 结束语 前言 爬取网络上的图片是一种常见的需求&#xff0c;它可以帮助我们批量下载大量图片并进行后续处理。本文将介绍如何使…

如何选择台式还是便携式多参数水质检测仪呢

选择台式还是便携式多参数水质检测仪主要取决于具体的使用需求和场景。 1.便携式多参数水质检测仪适用于需要在不同地点进行水质检测的情况&#xff0c;例如户外采样、实地调查等。它具有小巧轻便的特点&#xff0c;方便携带和操作&#xff0c;适合需要频繁移动或需要灵活使用的…

深入解析Linux进程内存:VSS、RSS、PSS、USS及查看方式

VSS 虚拟耗用内存大小&#xff0c;是进程可以访问的所有虚拟内存的总量&#xff0c;包括进程独自占用的物理内存、和其他进程共享的内存、分配但未使用的内存。 RSS 驻留内存大小&#xff0c;是进程当前实际占用的物理内存大小&#xff0c;包括进程独自占用的物理内存、和其…

bootstrap入门到精通

官网&#xff1a;列表组 - Bootstrap框架 (bootstrapdoc.com) 菜鸟教程&#xff1a;Bootstrap 列表组 | 菜鸟教程 (runoob.com) 1.什么是bootstrap bootstrap是一个用于快速开发web应用程序和网站的前端框架。基于HTML、CSS和JavaScript封装 响应式 ElementUI 更适合用于企…

WIZnet W5500-EVB-Pico DHCP 配置教程(三)

DHCP协议介绍 什么是DHCP&#xff1f; 动态主机配置协议DHCP&#xff08;Dynamic Host Configuration Protocol&#xff09;是一种网络管理协议&#xff0c;用于集中对用户IP地址进行动态管理和配置。 DHCP于1993年10月成为标准协议&#xff0c;其前身是BOOTP协议。DHCP协议由R…

Spring源码(五)— 解析XML配置文件(一) bean标签解析流程

前面几章的内容已经介绍了BeanFactory创建前的准备工作&#xff0c;以及加载XML配置文件前的准备的步骤。本章会着重介绍解析XML的步骤。 registerBeanDefinitions 前几个方法不做过多的赘述&#xff0c;着重看registerBeanDefinitions方法中解析XML的步骤。 public int regi…

C++ 多进程学习总结

C多进程 进程间通信 消息队列 消息队列&#xff1a;提供一个种进程间发送/接收数据块&#xff08;常为结构体数据&#xff09;的方法。 函数接口 ftok()&#xff1a;获取消息队列键值msgget()&#xff1a;创建和访问消息队列msgsnd()&#xff1a;向消息队列发送数据msgrcv…

罗布乐思Roblox学习笔记

罗布乐思 文章目录 罗布乐思基本操作CFrameGUIModule script呼吸灯商店imageChangetag标签知识答题showTips 基本操作 缩放按shift 等比例缩放 ctrl 双向缩放 复制对象 ctrlD &#xff08;如果选择多个对象&#xff0c;按住ctrl&#xff09; F 聚焦 Workspace ​ Terrain…