当前位置:   article > 正文

ue4 函数和宏区别_【UE4】通俗易懂 用蓝图来学习 C++ 基础知识

ue 宏和函数的区别

2f70e1bfa56591274e85a6bc90e75cdf.png

【前言】:

用老罗来学UE蓝图:

https://zhuanlan.zhihu.com/p/135297007​zhuanlan.zhihu.com

再开个脑洞,用蓝图来类比学习C++基础知识。

C++ 刚开始学,所以本篇知识点不是特别全有些地方也可能不是特别准,望谅解,以后会慢慢补上。如有错误麻烦大佬们指出,感激不尽。


【杂谈】:

另:多少菜鸟的噩梦啊。

dbe5b735e71ab79657d8ba36c838566a.png

之前就是这样学习的,从书上提取知识点笔记记了一大堆,想着把书从薄读到厚,再从厚读到薄。

c69dea6573169e8530ab25707acf46eb.png

结果发现屁用都没有,该写程序的时候啥都不会,琐碎的知识点实在太多了。

所以:

  1. 知行合一 ——实战啊,不要一上来就把一本书从头到尾看一遍。尤其是这种天书是用来查的,跟字典一样,不是用来看的
  2. 抓住重点,像那些比较难理解的知识点,琐碎的知识点以后会随着实战慢慢会补充上。
  • 总分——视频(教程)转文章——把别人的长视频(中英文)转化为文章,复习随时打开,既方便,逻辑又清晰,还省流量。
  • 分总——文章转图片——把别人的多文章转化为思维导图,将零碎重要知识点总结成一个框架以消化吸收,代码细节先不要管,先理解它有什么作用,含义,实战上手练习时再细究这些细节。

【导图】:

c6f2c2c4391d2b316e588caa25f50e47.png

cbc38b8af3f075d6af2ba1022a50eea5.png

一、 C++ Intro


【1.1】C++的诞生

Bjarne Stroustrup (来跟我一起唱,杯啊你 死招死咒噗~)唵嘛呢叭咪吽

它是在大佬头发郁郁葱葱的时候于1979年设计开发的,最初命名为带类的C,1983年更名为C++。C艹(四声、谢谢合作)对C进一步扩充和完善,而且随着时间发展会有不同的标准。

下面为五代标准。

  • 1998——C++98
  • 2003——C++03
  • 2011——C++11
  • 2014——C++14
  • 2017——C++17

你们这些大佬啊,再研究研究这就是你们以后的下场,标准发型!毫无回天之术 。

b30fa2183dc8612c9a675f52d7575c7e.png

【1.2】重要组成部分

  • 核心语言——提供所有构建块,包括变量、数据类型、常量等
  • C++ 标准库——提供大量函数,用于操作文件、字符串等等
  • 标准模板库(STL)——提供了大量的方法,用于操作数据结构等

【1.3】C++面向对象开发的四大特性

  1. Encapsulation 封装——数据封装将实现细节与相关数据分离开,防止函数直接访问类类型的内部成员。
  2. Abstract 抽象——抽象类不能被用于实例化对象,只能够用作接口使用,接口描述了类的行为和功能,而不需要完成类的特定实现。(如动物抽象类不能实例化出猴子、老虎这样的对象)。
  3. Inherited 继承——BaseClass基类 & DerivedClass派生类,继承允许我们依据一个类来定义另一个类,这使得创建和维护一个应用程序变得更容易,也达到了重用代码功能和提高执行效率的效果。
  4. Polymorphic 多态——有了多态,我们可以有多个不同的类,都带有同一个名称,但具有不同实现的函数,甚至函数参数可以相同。虚函数是C++中用于实现多态的机制。核心理念就是通过基类访问派生类定义的函数。

8f2dd1d9022608569821a73af95f04b4.png

二、C++基本语法


【2.1】Class类 & Object对象

基类——class Shape ;(有宽高属性)
基类的对象—— Shape sha; (类只有一个,但是对象可以有很多个,如Shape sha1、Shape sha2)
派生类——class Rectangle : public Shape ;(继承自基类,新添加了计算面积函数,宽乘高就行了)
派生类的对象——Rectangle Rect;

【01】:Class类

对同一类对象的共同属性和行为进行概括,形成类。比如人类、动物类、植物类等。如常有的属性是 颜色、大小、高低、重量、年龄等。

【02】:Object对象

对象Object,它是类的实例,用来特定类,所以是对象,常常定义属性的值

  1. #include <iostream>
  2. using namespace std;
  3. class Box
  4. {
  5. public:
  6. double length; // 长度
  7. double breadth; // 宽度
  8. double height; // 高度
  9. };
  10. int main()
  11. {
  12. Box Box1; // 声明 Box1 object对象,类型class为 Box
  13. // box 1 对象详述
  14. Box1.height = 5.0;
  15. Box1.length = 6.0;
  16. Box1.breadth = 7.0;
  17. // box 1 的体积
  18. volume = Box1.height * Box1.length * Box1.breadth;
  19. cout << "Box1 的体积:" << volume <<endl;
  20. return 0;
  21. }

可以结合一点玄学的东西。

https://zhuanlan.zhihu.com/p/72782575​zhuanlan.zhihu.com
c2399febfc5e42c118062ffd05f9d657.png

有一个白马非马的故事,感兴趣的可以了解一下。

  • 成立——白马属于马,所以白马不是马
  • 不成立——白马是马,但是白马并不能完全代表马
  • 成立也不成立——白马非马,但是白马不能够代表马这个概念啊。可白马真的是一匹马啊

放在C++这里,白马是对象Object,马是类(动物类的子类),继承inherited自动物类。

高中生物学的 界门纲目科属种 也适用,大道都是相通的。

当然还有一个 ,他大舅他二舅都是他舅,高桌子低板头都是木头。

【03】跟UE4对比学习

对应UE4的话,就是:

  • BP 从基类中创建蓝图,ParentClass 父类 就是基类,如Actor。
  • C++扩展类,ParentClass 父类为当前扩展类,如ShitActor,那么它是基于Actor之上继承的类,内容上相当于从Actor里git clone一样,我们在它的基础上添加一些新东西。

0e140ed3f9d225f0da003892c526df4d.png


编辑器右上角可以看到ParentClass,Class Settings中可以更改父类及相关属性。

947fc0800bfece6fa6d206bfb8c244db.png

0a378a2fcbee32d8228223750ed73c7c.png

我是这样类比的。

  • Content Browser 中的 BP————在UE中,只要是类,值改动无所谓,比如Character改动了Mesh、运动速度等。那么它始终就是个类,毕竟一直在Content Browser当中的嘛。
  • 世界场景中的BP——对象—— 一旦被放入到世界场景当中,就是对象,如拖拽了两个一摸一样的BP,细节面板中可以分别调节它们的相关参数,可以不一样。

1a30f25f1318d95cf6a98317bb1f5bd6.png

而拿材质来说的话,材质就是类,材质实例就是对象,一个定义变量属性,一个定义值。

fbd056713dbf53131811be4dc691e6c6.png

【2.2】预处理器 Preprocessor(它实际上也是一种宏 Macros)

宏是一种批量处理的称谓。
计算机科学里的宏是一钟抽象,它根据一系列预定义的规则替换一定的文本模式,解释器或编译器在遇到宏时会自动进行这一模式替换。
在程序编译时将宏名(#define XXX)替换成字符串的过程被称为 ”宏展开“
  • 是啥——预处理器是一些指令,指示编译器在实际编译之前所需要完成的预处理
  • 注意——预处理器指定不是C++语句,所以它们不会以;分号断句。所有的预处理器指令都以#井号开头
  • 作用——将所包含的文件全文复制到#include的位置,相当于是个展开为一个文件的宏
  • 常见预处理器指令
    • #include——相当于复制粘贴文件内容
    • #define——用于创建符号常量,该符号常量通常称为宏
    • #pragma once——所在文件仅编译一次
    • #ifndef A #define A #endif——可能会有多个cpp文件同时包含一个h头文件,避免头文件的重定义,出现大量重定义的错误

【2.3】NameSpace 命名空间

它定义了一个范围,以避免调用多个库中出现的同名函数。

  1. #include <iostream>
  2. using namespace std;
  3. // 第一个命名空间
  4. namespace first_space{
  5. void func(){
  6. cout << "Inside first_space" << endl;
  7. }
  8. }
  9. // 第二个命名空间
  10. namespace second_space{
  11. void func(){
  12. cout << "Inside second_space" << endl;
  13. }
  14. }
  15. using namespace first_space; //使用第一个名称空间
  16. int main ()
  17. {
  18. // 调用第一个命名空间中的函数
  19. func();
  20. return 0;

524d0409617030420ba403e44ce558a8.png

三、 .h(Header) 头文件和. cpp(C Plus Plus)源文件

类和对象分别对应了属性和值,而头文件和源文件分别对应了声明和实现。

  • 头文件.h相当于领导(header),对应阳,起主导作用。对员工发号施令(声明一个大饼),以及负责外部商贸工作(#include 借力,给团队拉活儿,保证团队活下去)。
  • 源文件 .cpp相当于员工 ,起配合作用。接收领导的指令,996 地边吃着大饼,边实现老板的要求。

3b814c6b1528f15c843efceefcda64bb.png

85c7a291f69d12a34a8d9f098387bde5.png

四、Class Access Modifier 类访问修饰符(public、private、protected)

  • 作用——它防止函数直接访问类类型的内部成员
  • 断句——每个标记区域(如private:)在下一个标记区域(如 public:)开始之前或者在遇到类主体结束右括号( int main(){ } )之前都有效。

【4.1】Public 公有成员

公有成员在类的外部是可以Get 和 Set 的。

5887077bb15c47b3e3c7a9387d40c52e.png

【4.2】Protected 受保护成员

保护成员在派生类(子类)中是可以Get的。
  • 对于蓝图事件图表中的变量来说,就是Variable下的Private,勾选后,子类中只能Get 变量值,而不能够Set 变量值。(Event Graph 中变量的Private其实就相当于C++的 Protected)

8e9e83285ef227664d39e703f9044254.png

【4.3】Private 私有成员

私有成员变量或函数在类的外部是不可以访问的,只能够被本类成员(类内)和友元函数Get 。 (C++ 成员和类的默认访问修饰符是private,而BP蓝图默认访问修饰符是public)
  • 对蓝图函数变量来说,就是函数Function/构造脚本内的Local Variable,只能供函数内部使用。

42d64bd1e67560662807c05cfa213998.png
  • 对材质来说,就是Constant,只供材质使用。而不是ScalarParameter可以在实例中(对象)调节参数。

a5f729ebc64bcf21a4e1e6ad04ad2a48.png

【4.4】不同的继承方法

  • 注意继承用 : 冒号继承
  1. /*public*/
  2. class A{};
  3. class B: public A{};
  4. /*protected*/
  5. class A{};
  6. class B: protected A{};
  7. /*private*/
  8. class A{};
  9. class B: private A{};
  • public:类内、类的对象;派生类、派生类对象 都可以访问
  • protected: 类内、派生类内 可以访问;类的对象和派生类的对象 不可访问

对象不可以访问,意思就是把类拽到关卡中变成对象的时候,get 不了对象的变量。

c56c36357f8f02021dda9557ffefc8a1.png
  • private: 只有类内部可以访问;类的对象、派生类、派生类的对象,统统不可访问。(就上面【4.3】列举过的,一样)

526c96da3c3ffca2ce19c9315c39e7db.png

五、Variable 变量


【5.1】基础变量类型

  • int—— 整数,不带小数,占4byte(32bit)空间的整数,最大值为2的31次方-1
  • float——浮点型,带小数
  • double——双精度浮点值,占8byte(64bit)空间的整数,最大值为2的63次方-1
  • char——占1byte的字符,取值范围为-128~127或0~255
  • void——无类型/空类型/不输入返回任何值
  • bool——true false

09a489a5391d3452cfdab3bf8a1d4460.png

【5.2】其他变量类型

【1】Constant常量

  • 说明——常量是固定值,在程序执行期间不会改变,且定义后不能进行修改。
  • 两种声明方式
    • #define 预处理器 —— #define LENGTH 10
    • const 关键字—— const int LENGTH=10;
  • 注意—— 分号断句、=等号、变量类型 这三点的区别
  • 安全检查——const 常量具有类型,编辑器可以进行安全检查,#define宏定义没有数据类型,只是简单的字符串替换,不能进行安全检查

【2】Array数组

  • 说明——它用来存储一系列数据,但往往被认为是一系列相同类型的变量
  • 声明——int a[5]; 声明5个类型为int的数字
  • 访问单个数组元素——int b=a[0]——通过索引访问数组的第一个元素(index=0)

【3】Enum枚举

  • 说明——它是由用户定义的若干枚举常量的集合
  • 声明——
    • enum color{red,green=5,blue}; ——索引从0开始,red值为0,green值为5,blue值为6
    • enum color{red,green,blue}c; c=blue;—— 变量类型为color的c,然后c被复制为三个颜色中的一个blue蓝色

【4】String字符串

  • 说明——字符串
  • 声明——char str[n]="xxxxxxxx";
  • 其他——
    • strcpy(a,b)—— 把b的字符串复制给a
    • strcat(a,b)—— 连接a b 字符串

【5.3】typedef

为已有类型取一个新名字,如用int声明整数换成用a声明整数

  1. typedef int a;
  2. a shit= 0 ;

【5.4】变量作用域

  • local variable——局部变量——在函数或代码块内部声明的变量——如 int main(){}主函数 { } 括号内声明的变量(上面提到过了,对应蓝图BP 函数Local Variables)
  • global variable——全局变量——所有函数外部声明的变量——如 int main(){} 主函数前面声明的变量(非函数中定义的变量,如Event Graph中定义的全局变量)
  • 形式参数——在函数参数的定义中声明的变量——如 int func (int a);

b43499a8581c55b027acdb7a120b3103.png
形式参数

【5.5】存储类

存储类定义C++程序变量/函数的范围(Visibility)和生命周期(lifetime)

【1】Static

  • 作用——static 修饰局部变量可以在函数调用之间保持局部变量的值,而不需要在每次它进入和离开作用域时进行创建和销毁

【2】Extern

  • 作用——extren 存储类用于提供一个全局变量的引用。全局变量对所有程序文件都是可见的,当使用extren时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置

01894f70e94c6889a233c32897cd3e4e.png

95774a15d20eac01d84fb7d2c70f13cb.png

六、Statement 语句

没啥说的了,看老罗BP吧。相关的循环语句条件语句蓝图版在老罗那篇。

【6.1】循环语句

  • while
  • for
  • do while
  • 嵌套循环
  • break
  • continue
  • goto

【6.2】条件语句

  • if
  • if else
  • if 嵌套
  • switch
  • switch嵌套

868555c38d95fffc87b59738bdea04ca.png

七、Pointers 指针


【7.1】指针

  • 定义——指针是一个变量,其值为另一个变量的地址。每一个变量都有一个内存位置,每一个内存位置都定义了可使用& 连字号 运算符访问的地址,它表示了在内存中的一个地址
  • 符号——
    • 访问地址符号——&——它可以输出变量在内存中的地址
    • 声明指针符号——*——用来声明一个指针储存地址值
  • 注意事项——
    • 所有指针的值的实际数据类型(int、float、char等等)都一样,都代表了内存地址的长的16进制数
    • *ip=n——带星号输出变量值
    • ip=&n=0x0000001——不带星号输出地址值
  1. int a; //&a表示地址
  2. cout<< &a<<endl;
  3. int *a=5; //声明一个指针
  4. cout<<a<<endl; // 输出地址

【7.2】NULL 空指针

  • 定义——声明变量时,如果没有确切地址可以赋值,通常为指针赋一个NULL值
  • 语法——
  1. int *ptr=NULL;
  2. cout<<ptr<<endl;
  3. // 输出地址值 00000000
  • 注意——
    • 访问地址为0的内存是为操作系统保留的
    • 内存地址0表示该指针不指向一个可访问的内存地址
    • 如果所有未使用的指针都被赋予空值,同时避免使用空指针,就可以防止误用一个未初始化的指针

【7.3】指针的算数运算

int32表示32位的整数,而1byte=8bit,所以它占用4个字节,对应内存地址就类似这样,1000递增变1004;而char字符占用1个字节,地址 1000递增变1001

ptr++ (ptr--)

2be0f211d7a0404e6bc37f7c20ad34e1.png

八、Functions 函数

【8.1】class functions 类成员函数

  • 定义——它是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员
  • 语法——
  1. //类内定义
  2. class Box
  3. {
  4. output funcname (input){}
  5. }
  6. //类外定义,中间加 类名和一个范围解析运算符 即可
  7. output Classname:: funcname (input)
  • 符号——范围解析运算符(用于类外定义) ::双冒号
  • 格式——

void如果是空的话,可以不用填写

  1. output funcname (input){}
  2. int main(){
  3. return 0;
  4. }

【8.2】constructor 构造函数

类似Construction Script构造脚本。

https://zhuanlan.zhihu.com/p/134279765​zhuanlan.zhihu.com
  • 定义——类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行
  • 注意——
    • 构造函数的名称与类的名称是必须完全相同的
    • 并不会返回任何类型,也不会返回void
  • 作用——
    • 构造函数可用于为某些成员变量设置初始值
    • 主要用来创建对象时初始化对象,即为对象成员变量赋初始值
  • 格式——
  1. // 不带参数
  2. class A
  3. {
  4. public:
  5. A(); // 声明构造函数
  6. };
  7. A::A(void){}; //定义构造函数
  8. //带参数
  9. class A
  10. {
  11. public:
  12. A(int len); // 声明构造函数
  13. };
  14. A::A(int len){}; //定义构造函数
  • 使用初始化列表来初始化字段
  1. C::C(int a,int b,int c):X(a),Y(b),Z(c){}
  2. //等同于
  3. C::C(int a,int b,int c)
  4. {
  5. X=a;
  6. Y=b;
  7. Z=c;
  8. }

【8.3】deconstructor 析构函数

  • 定义——在每次删除所创建的对象时执行
  • 注意——析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号 ~ 作为前缀
  • 作用——析构函数有助于在跳出程序(如关闭文件、释放内存等)前释放资源
  • 格式——
  1. class A{
  2. public:
  3. A(); //声明构造函数
  4. ~A(); //声明析构函数
  5. A::A(void){}; //定义构造函数
  6. A::~A(void){}; //定义析构函数

【8.4】copy constructor 拷贝构造函数

  • 定义——它在创建对象时,是使用同一类中之前创建的对象类初始化新创建的对象
  • 作用——
    • 它在创建对象时,是使用同一类中之前创建的对象类初始化新创建的对象。
    • 复制对象把它作为参数传递给函数
    • 复制对象,并从函数返回这个对象
  • 格式——
  1. classname(const classname &obj){}
  2. //其中 obj是一个对象引用
  3. A(const A &obj);

【8.5】friend 友元函数

  • 定义——类的友元函数是定义在类外部,有权访问类的所有私有(private)和保护(protected)成员
  • 注意——尽管友元函数的原型有在类的定义中出现过,但是友元函数不是类成员函数
  • 格式——
  1. class Box{
  2. public
  3. friend void print(Box box); //定义友元函数
  4. };
  5. void print(Box box){}; //声明友元函数
  6. int main(){
  7. Box box;
  8. box.变量=n;// 访问对象中的变量
  9. print(box);
  10. }

【8.6】inline 内联函数 (默认就是内联的)

  • 定义——如果一个函数是内联的,那么编译时编译器会把该函数的代码副本位置放在每个调用该函数的地方
  • 注意——
    • 在类定义中的定义的函数都是内联函数,即使没有inline说明符
    • 内联函数一般都是1~5行的小函数
    • 内联函数中不允许使用loop循环和switch开关语句
    • 内联函数的定义必须出现在内联函数第一次调用之前
  • 作用——
    • 编译时把函数的定义替换到调用的位置
    • 解决程序中函数调用的效率问题

【8.7】virtual void 虚函数

【1】虚函数

  • 为什么虚——并不能确定被调用的是基类的函数还是派生类的函数,所以被成为”虚“函数
  • 哪儿虚?——虚在推迟联编或动态联编上,一个类函数的调用并不是在编译时刻确定的,而是在运行时刻被确定的
  • 定义——虚函数是在 基类中使用 关键字 virtual声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态连接到该函数
  • 作用——定义为虚函数是为了允许 用基类的指针来调用子类的这个函数
  • 注意——
    • 定义一个函数为虚函数,不代表函数为不被实现的函数
    • 定义一个函数为纯虚函数,才代表函数没有被实现

【2】纯虚函数

  • 定义——纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法
  • 作用——定义纯虚函数是为了实现一个接口,起到一个规范的作用
  • 通俗——健林口袋里的一个小目标对自己来说不算啥,但是要求任何思聪都要有自己的花法,是正经地投资?还是花天酒地泡妞。每个思聪都有自己的花法,健林不闻不问。
  • 格式——
virtual void func()=0
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/127226
推荐阅读
  

闽ICP备14008679号