[3] C++面向对象编程
Day1
函数指针数组
简写函数指针 typedef
typedef int (*FunPtr)(int, int);
FunPtr FunArr[1] = {Add};
内联函数
#pragma region 内联函数
// 避免函数跳转对于程序的额外开销
// 有两种写法 1).h中写实现文件(在.h中同时写声明和实现)
// 2)inline 关键字
// 如果声明和实现拆开,都需要加inline
// 声明和实现写道一起,然后加inline(推荐)
// 注意点 到底是不是内联函数取决于编译器自己决定
// 即使按照上面的写法,编译器也可以不认为这是一个内联函数
// 即使不按照上面的写法,编译器可以认定这是一个内联函数
// 内联函数中不允许出现循环和switch,代码不能超过十行,递归函数不能是内联函数
// 存取函数 推荐写成内联函数 GetXX SetXXX
// 本质 在调用内联函数时编译器会对内联函数进行展开替换
// 虚幻中可以强制指定某个函数必须是内联
//cout << TestAdd(1, 2);
//cout << TestAdd(1 ,2) << endl;
#pragma endregion
// 声明和实现拆开
inline int TestSubtract(int Num1, int Num2);
// 声明和实现写在一起
inline int Multiply(int Num1, int Num2) { return Num1 * Num2; }
宏
#pragma region 宏
// #define 定义的叫做宏 Macro
// 宏的本质是单纯的替换,不检查类型
// 宏的替换出现在预编译阶段(预处理指令)
// 宏一般都是全大写字母,有的时候需要加上_区分 _FILENAME_ FILE_NAME __FILENAME__(系统保留宏)
//Divide(10, 5);
cout << __FILE__ << endl; // 表示当前文件的完整路径
cout << __LINE__ << endl; // 表示代码所在的行数
cout << __LINE__ << endl;
//常量宏 参考 Util.h
//宏中带参数
cout << ADD(10, 20) << endl;
cout << ADD(10, 20);
int Result = 10 % MULTIPLY(50, 2);
Result = 10 % 50 * 2;
cout << Result << endl; // 10
int Max = MAX(10, 50); // (((10) > (50)) ? (10) : (50))
cout << Max << endl;
//声明
// 如果宏带参数的 参数和计算结果用()括起来
#define MULTIPLY(Num1, Num2) ((Num1) * (Num2))
// 用inline函数代替带参数的宏
// inline int Multiply(int Num1, int Num2) { return Num1 * Num2; }
#define MAX(Num1, Num2) (((Num1) > (Num2)) ? (Num1) : (Num2))
#define _COUT_(Begin, End) for(int i = Begin; i < End; ++i) \ //宏定义函数
{\
cout << i << endl;\
} // 最后一行不需要加 (\)
#define _APPEND_(Name) "Hello, "##Name //字符串拼接
#pragma endregion
常量
#pragma region 常量
//// 常量 一旦初始化完成就不能改变 const
const int Num = 100; // 常量在声明的时候必须初始化,否则报错
Num = 150; // 不允许改变值
int Num1 = 100;
const int Num2 = Num1; // 可以用变量取初始化常量,变量的值改变不会影响常量
Num1 = 1000;
cout << Num2 << endl;
int Num3 = 5000;
//const跟指针变量连用 三种情况
int* pNum = &Num1; // 1000
const int* pNum1 = &Num1;
pNum1 = &Num2; // 可以指向别的变量
*pNum1 = 3000; // 不能通过解引用修改值
//int* const pNum2 = &Num3;
//// pNum2 = &Num1; // 不可以指向别的变量
//*pNum2 = 8000; // 可以通过解引用修改值
const int* const pNum3 = &Num2;
pNum3 = &Num1; // 不能指向另外一个变量
*pNum3 = 500; // 不能通过解引用修改值
#pragma endregion
引用
&引用内存地址 :
引用地址和内存的关系
引用注意点
引用符号&和指针符号*的区别
#pragma region 引用
//int Num = 50;
//int& Num1 = Num;
//Num1 = 123;
////cout << &Num << " " << &Num1 << endl; //引用的地址
////cout << Num << " " << Num1; // 地址里面的值
//int Temp = 5000;
//Num1 = Temp;
//cout << Num << " " << Num1 << endl; //因为Num的地址和Num1的内存的地址是一样的 , 101行会影响到 Num的值
//Num1 = 500; // Num也同步被更改了
//cout << Num << " " << Temp << endl;
//cout << &Num1 << " " << &Num << " " << &Temp << endl;// Num1 Num地址一样 Temp不一样;
//----------
int Num1 = 50;
int Num2 = 500;
//调用的时候不传递地址 , 函数接受用 & 取地址
//Swap(Num1, Num2);
//cout << &Num1 << " " << &Num2 << endl; //地址和函数内一样
//调用的时候不传递地址
//Swap1(Num1, Num2);
//cout << &Num1 << " " << &Num2 << endl; //地址和函数内一样
//调用的时候传地址
//cout << &Num1 << " " << &Num2 << endl << endl; //
//Swap(&Num1, &Num2);
//cout << Num1 << " " << Num2 << endl;
//--------字符串引用
cout << TestStr("hello"); //这种引用函数里面需要加 const
string Str = "hello";
cout << TestStr(Str);
#pragma endregion
左值右值
#pragma region 左值右值
// 左值 可以放到 = 左边的
// 右值 不可以放到 = 左边的(字面量 常量和指针连用)
// 右值引用 int&&
int Num = 10;
Num = 100;
// int&& rNum = Num; // 不能把变量赋值给右值引用
int&& rNum = 10; // 右值引用只能赋值字面量(右值)
#pragma endregion
Day2
枚举
#pragma region 枚举
//普通创建-------
enum EMuDay
{
EMD_Weekday = 1, //
EMD_Weekend,
};
EMuDay MyDay = EMD_Weekday; //声明一
EMuDay MyDay = EMuDay::EMD_Weekend; //声明二
cout << MyDay << endl;
类创建-------
enum class Eseason
{
ES_Spring,
ES_Summer,
ES_Autuem,
ES_Winter,
};
int Num = 10;
Eseason MyDay2 = (Eseason)Num;
Eseason MyDay3 = Eseason::ES_Summer;
cout << (int)MyDay2 << endl;
cout << (int)MyDay3 << endl;
Eseason Season1 = Eseason::ES_Spring;
#pragma endregion
面向对象
#pragma region 面向对象
// 需要先有类,才能有对象
// 一个类理论上可以创建无限个对象,每个对象之间并没有直接关系
// 面向过程 面向对象 面向组件
// 面向对象 封装 继承 多态
// 类中可以包含什么内容 1)属性(成员变量) 2)函数(事件)
#pragma endregion
#pragma region 类的基本使用
// 创建类()
// 创建对象(实例化)
#pragma endregion
类的基本使用
class Person
{
friend void GetPerson(Person P);
public:
//Person() 默认调用无参构造函数
//有参就为有参函数
Person(int InID, string InName, int InAge);
int ID;
string Name;
//inline void SetAge(int InID) { ID = InID < 0 || InID > 1000 ? 0 : InID; }
inline void SetAge(int InAge) { Age = InAge < 0 || InAge > 150 ? 0 : InAge; }
inline int GetAge() { return Age; }
protected:
private:
int Age;
};
Person P1;
APerson P2 = APerson();
APerson* P3 = &P1;
APerson* P4 = new APerson();
P1.SetAge(10);
GetPerson( P1); //调用友元
P1.SetAge(20);
cout << endl << P1.GetAge();
友元
##Person.h
class Person
{
friend void GetPerson(Person P); //友元函数声明
public:
int ID;
string Name;
//inline void SetAge(int InID) { ID = InID < 0 || InID > 1000 ? 0 : InID; }
inline void SetAge(int InAge) { Age = InAge < 0 || InAge > 150 ? 0 : InAge; }
inline int GetAge() { return Age; }
protected:
private:
int Age;
};
##Day2.cpp
void GetPerson(Person P) {//友元函数定义
P.Age = 15;
cout << P.Age;
}
GetPerson( P1); //调用友元 15
P1.SetAge(20); // 不会影响到person
有元类函数
封装和访问修饰符
#pragma region 封装和访问修饰符
// // 访问权限 默认权限是private
// // 结构体默认在外部是可以访问的,但是类默认在外部无法访问成员
// // public 无论在类内部还是在子类内部,还是在类外部都可以访问
// // protected 是在本类的内部和子类的内部可以访问
// // private 是在本类的内部可以访问
//Person P1;
//cout << P1.IsEmpty() << endl;
//P1.ID = 10010;
//P1.Name = "张三";
//P1.SetAge(18);
//P1.Print();
///*P1.ID = 10010;
//P1.Name = "张三";
//P1.SetAge(145);*/
//// P1.Age = 5000;
//// cout << P1.ID << " " << P1.Name << " " << P1.GetAge() << endl; // 类的成员默认都是垃圾值
//// 封装 把属性私有化,把函数公开化 属性变量的读写权限的限制(public Get private Set)
//// 把该公开的公开,把该隐藏的隐藏
//// 友元 破坏封装
//// 全局友元函数 成员友元函数 友元类
////
//// 友元函数 在类的内部 friend + 函数声明,访问权限无所谓 -> 在函数内部可以访问对象的私有成员或函数
//// 先包含两个头文件
//// 先创建人的对象 再创建宠物的对象, 让两者产生关联
//Human H;
//// H.Name = "王五";
//Pet P;
//// P.NickName = "天下无敌";
//H.MyPet = &P;
//P.Owner = H;
//H.DispleyPet();
//P.DisplayOwner();
#pragma endregion
构造函数
//--Person.cpp
#include "Person.h"
using namespace std;
Person::Person()
: CNum(0)
{
cout << "无参构造" << endl;
ID = 0;
Name = "";
Age = 0;
}
Person::Person(int ID, int InCNum)
:CNum(InCNum), ID(ID)
{
// this相当于蓝图中的this,
// this是一个地址,指向当前对象
this->ID = ID;
Name = "";
Age = 0;
}
Person::Person(int InID, std::string InName, int InAge, int InCNum)
:CNum(InCNum), ID(InID), Name(InName), Age(InAge)
{
}
//--Person.h
#pragma once
#include <iostream>
// class是关键字 类名 {}括号内写属性和函数 最后;别忘了
class Person
{
friend void TestPerson(Person P);
public:
Person();
Person(int ID, int InCNum);
Person(int InID, std::string InName, int InAge, int InCNum);
const int CNum;
int ID;
std::string Name;
// 设置和获取年龄的函数
inline int GetAge() { return Age; }
inline void SetAge(int InAge) { Age = InAge < 0 || InAge > 150 ? 0 : InAge; }
// 判断是否为空
bool IsEmpty();
// 数据置空
void Empty();
// 显示信息
void Print();
protected:
private:
int Age;
};
//--Day.cpp
#pragma region 构造函数
// 在C++类中有几个函数比较特殊 构造函数 拷贝构造函数 析构函数 赋值运算符
// 构造函数 声明和初始化对象的时候调用
// 拷贝构造
// 析构函数 对象被销毁的时候调用
// 构造函数语法 没有返回值 函数名和类型完全相同 一般是public的
// 构造函数支持重载
// 编译器自动提供一个无参构造函数,如果程序员写了任意一个构造函数那么编译器就不在提供默认无参构造函数
// 注意点 如果要写构造函数,先写一个无参构造,然后再去写其他的重载
Person P;
Person P1 = Person();
// 隐式构造(编译器自动提供的 无参构造)和显式构造(程序员自己写的)
// 但凡有任何一个显式构造,就不会提供任何隐式构造
Person P2 = Person(10010, "张三", 15, 500);
P2.Print();
// Person P3 = Person(10010, "张三");
// 如果类中有常量,常量的初始化不能写到构造的{}中,需要借助 初始化列表
// 初始化列表的执行时机早于构造函数的函数体{}
#pragma endregion
浩:成员函数指针
Day3
拷贝构造
代码
#pragma region 拷贝构造
// 拷贝构造是构造函数的一种特殊形式
// 基本语法 没有返回值,拷贝构造参数格式是固定的,拷贝构造没有重载
// 类名(const 类名& Another);
// 拷贝构造 编译器会自动提供,除非程序员手动编写拷贝构造
// 作用 B对象的属性挨个拷贝给A对象
// 调用时机
// 1)通过一个已经存在的对象去初始化另外一个对象 APerson P2 = p1; ,但是初始化完成之后的赋值语句不会调用拷贝构造
// 2)当函数参数进行传递的时候使用的普通类型(非引用和指针),实参拷贝给形参的过程会发生拷贝构造
// 3)如果函数的返回值是一个普通类型
// 1、如果函数直接返回全局变量,会发生拷贝构造(返回值传递)
// 2、如果直接返回普通形式的形参,会发生两次拷贝构造(参数传递 返回值传递)
// 3、如果返回函数体中的一个临时变量 也会发生拷贝构造(返回值传递)
//
// // 如果类型使用普通类型,有可能发生拷贝,但是如果使用的是引用或者指针的时候就不会发生拷贝
// 重写 如果类属性没有指针,不需要重写拷贝构造
//APerson P1 = APerson(10010, "王五");
////APerson P2 = P1; // APerson P2(P1);
////P2 = P1; // 属于赋值,不会调用拷贝构造,会调用赋值运算符
////APerson P3;
////P3 = P2; // 属于赋值,不会调用拷贝构造
////P2.Display();
//// 形参是普通类型,实参的值给到形参(拷贝构造),再去函数体中做运算
//// TestPerson(P1);
//APerson& Temp = TestPerson1();
#pragma endregion
析构函数
代码
//--Day.cpp
#pragma region 析构函数
// 作用 对象被销毁的时候调用析构
// 一般情况下不需要重写析构,类中成员出现了指针 new delete
// virtual ~类() 只有这一种形式,析构函数没有重载
// 调用时机 当对象销毁的时候(栈局部变量自动销毁 delete)
//{
// //APerson P = APerson(10010, "乔峰"); // 调用无参构造和析构
// //APerson P1 = APerson(10011, "虚竹");
// // 栈空间有一个特点 先进后出 对于局部变量,先申请后销毁
// int Num = 100;
// // cout << &Num << endl;
// APerson P1 = APerson(10010, "乔峰", Num);
// APerson P2 = P1;
// P1.Display();
//}
#pragma endregion
//--Day.cpp
APerson::~APerson()
{
// 一般是用来清理new出来的空间, delete nullptr
// cout << Name << " 调用析构" << endl;
if (Ptr)
{
delete Ptr;
Ptr = nullptr;
}
}
带有指针的拷贝构造和析构函数
没有深拷贝下的内存图
深拷贝 this->Ptr = new int(*Another.Ptr);
//--在调用拷贝构造的时候吧值也深拷贝过去
// 通过拿去Another的值给this去赋值
// const &
// 如果形参是引用:1)函数内外同时修改 2)避免多拷贝一块内存
// const& 1)避免拷贝同一块内存 2)避免在函数内部修改形参(实参)
APerson::APerson(const APerson& Another)
{
// Another.ID = 500;
cout << "调用拷贝构造" << endl;
this->ID = Another.ID;
this->Name = Another.Name;
// this->Ptr = Another.Ptr; // 发生浅拷贝 指针的地址直接赋值过去
// 深拷贝 重新开辟一块内存空间,然后把内存中对应的值拿过来
Ptr = new int(*Another.Ptr); // 直接开辟空间同时进行赋值
//Ptr = new int(); // 开辟空间
//*Ptr = *Another.Ptr; // 赋值
cout << "调用拷贝构造" << endl;
}
深浅拷贝内存图
继承
继承关系和访问修饰符
Day.cpp
//--Day.cpp
#pragma region 继承
// 父类 (基类 超类) 子类(派生类 衍生类)
// 不要对看似有关系的类做继承关系 鸟会飞 飞机也会飞 (飞机继承鸟)
// 飞机 机械类 接口
// 继承语法和注意点
// 必须包含父类的头文件,不能使用父类的前置声明
// 使用 : 宣告继承关系 public不能省略,如果省略会变成private继承
AStudent Student;
Student.SetID(10011);
Student.SetName("佩奇");
Student.Display();
// (函数)重载 函数重定义 函数重写
// 函数重定义 在继承中,子类重新定义父类中存在的方法
APerson P1 = APerson(10010, "小乔", 50);
P1.Display(); // 父类指向父类会调用父类的操作
AStudent S1 = AStudent();
S1.SetID(10011);
S1.SetName("小小乔");
S1.Display(); // 子类类型指向子类对象调用子类
APerson P2 = S1; // 父类类型指向子类 鸟 = 麻雀
P2.Display(); // 父类指向子类,调用父类
APerson& P3 = S1;
P3.Display(); // 父类引用指向子类,调用父类
APerson* P4 = &S1;
P4->Display(); // 父类指针指向子类,调用父类
在重定义模式下无论父类指向父类还是父类(引用和指针)指向子类都是调用父类的方法
如果在重定义中想要调用父类的逻辑需要添加 Parent::FunctionName();
// 重写 在父类的方法中加virtual && 在子类的方法中加override,virtual可带可不带(推荐带上)
APerson P1 = APerson(10010, "阎王爷", 40);
P1.Show(); // 父类对象指向父类,调用父类
AStudent S1;
S1.SetID(10012);
S1.SetName("黑无常");
S1.Show(); // 子类类型指向子类,调用子类
APerson P2 = S1;
P2.Show(); // 父类对象指向子类,调用父类,但是属性是子类的
APerson& P3 = S1;
P3.Show(); // 父类的引用指向子类,调用子类,属性也是子类的
APerson* P4 = &S1;
P4->Show(); // 父类的指针指向子类,调用子类,属性也是子类的
// 重写 普通父类指向父类或者子类,调用父类的方法;父类的引用或者指针指向子类,调用子类的方法
#pragma endregion
Person
//--Person.h
#pragma once
#include <iostream>
class APerson
{
protected:
int ID;
std::string Name;
int* Ptr;
float Height;
public:
std::string PublicTest;
APerson();
APerson(int ID, std::string Name, int Num);
APerson(const APerson& Another);
inline int GetID() { return ID; }
inline void SetID(int ID) { this->ID = ID; }
inline int* GetPtr() { return Ptr; }
inline std::string GetName() { return Name; }
inline void SetName(std::string InName) { Name = InName; }
void Display();
virtual ~APerson();
virtual void Show();
};
//--Person.cpp
#include "Person.h"
using namespace std;
APerson::APerson()
: ID(0), Name("")
{
Ptr = new int(0); //
// string* Str = new string("Hello");
}
APerson::APerson(int ID, std::string Name, int Num)
{
this->ID = ID;
this->Name = Name;
// 第一种写法
this->Ptr = new int(Num); // new一块空间,存储Num的值 *Ptr = Num
// 第二种写法
/*Ptr = new int();
*Ptr = Num;*/
}
APerson::~APerson()
{
// 一般是用来清理new出来的空间, delete nullptr
// cout << Name << " 调用析构" << endl;
if (Ptr)
{
delete Ptr;
Ptr = nullptr;
}
}
void APerson::Show()
{
cout << "父类的Show()" << Name << endl;
}
// 通过拿去Another的值给this去赋值
// const &
// 如果形参是引用:1)函数内外同时修改 2)避免多拷贝一块内存
// const& 1)避免拷贝同一块内存 2)避免在函数内部修改形参(实参)
APerson::APerson(const APerson& Another)
{
// Another.ID = 500;
cout << "调用拷贝构造" << endl;
this->ID = Another.ID;
this->Name = Another.Name;
// this->Ptr = Another.Ptr; // 发生浅拷贝 指针的地址直接赋值过去
// 深拷贝 重新开辟一块内存空间,然后把内存中对应的值拿过来
Ptr = new int(*Another.Ptr); // 直接开辟空间同时进行赋值
//Ptr = new int(); // 开辟空间
//*Ptr = *Another.Ptr; // 赋值
cout << "调用拷贝构造" << endl;
}
void APerson::Display()
{
if (ID > 0)
{
cout << "父类 " << ID << ": " << Name << " " << *Ptr << endl;
}
}
Day4
多态
多态
//--类声明
class AHuman
{
public:
AHuman() { std::cout << "构造" << std::endl; }
virtual void Walk();
virtual void Sleep();
virtual ~AHuman() { std::cout << "析构" << std::endl; }
};
class AChild : public AHuman
{
public:
virtual void Walk() override;
};
class Adult : public AHuman
{
public:
virtual void Walk() override;
};
class OldMan : public AHuman
{
public:
virtual void Walk() override;
};
#pragma region 多态
// 重写
// 多态现象 多种形态 同一种行为不同的对象调用会有不同的行为表现
// 走路 小孩 成年人 FC女子 老年人
// 发生条件 父类指针(引用)指向子类对象,调用virtual方法
// 面向未来变成的方式
AChild AC = AChild();
OldMan OM = OldMan();
ProcessMovement(OM);
//AHuman H1 = AHuman();
//H1.Walk(); // 父类
//AHuman H2 = AChild();
//H2.Walk(); // 父类
//AChild Child;
//AHuman& H3 = Child;
//H3.Walk(); // 子类
//AHuman* H4 = new OldMan();
//H4->Walk(); // 子类
//delete H4;
//H4 = nullptr;
// 多态的本质
/*AHuman H;
AHuman H1;
AChild AC;
AChild AC1;
cout << "eND" << endl;*/
// 类中有virtual修饰 才会有 __vfptr virtual function pointer(虚函数表)
// 同一个类的不同对象之间 __vfptr是一致的
// 每多一个virtual函数,__vfptr中多一个元素,如果没有virtual函数,__vfptr不显示的
// 多态的本质
// 类中有virtual修饰的函数,类本身会创建一个__vfptr变量,存储所有的virual
// 每创建一个对象,都会从类拿__vfptr 真正的对象才能完成调用自己的virtual函数
// 静态多态(函数重载,编译阶段)和动态多态(多态,运行阶段)
//
#pragma endregion
多态继承问题
多态的本质
结构体和类对比
结构体和类对比
#pragma region 结构体和类对比
{
// 结构体支持构造和析构
// 结构体支持继承
// 结构体支持重写
// 在CPP中结构体和类 99%相似, C++ 面向对象 泛型 C# C++++
// 结构体和类最大的不同:访问权限的问题
// 结构体和类怎么选择 ??
// Vector Rotator Transform结构体(简单的数据类型)
// Actor Component ActorComponent SceneComponent(复杂的数据类型)
/*FStudent FS;
FS.Name = "张三";
cout << FS.Name << endl;*/
/* FPerson* P = new FStudent();
P->Display();
delete P;
P = nullptr;*/
}
#pragma endregion
共用体/联合体 - 面试 : 计算结构体or共合体占用内存字节
共用体/联合体
//--声明
struct MyStruct
{
int Num1;
int Num2;
int Num3;
bool bResult;
};
union MyUnion
{
int Num;
bool bSuccess;
float Height;
long long LNum;
// string Str;
// AHuman H;
// FPerson FP;
MyStruct MS;
};
struct MyStruct1
{
MyUnion MU; // 16
int Age; // 8
float Height;
char C; // 8
int Num1;
char Cs[6]{}; // 8
};
#pragma region 共用体/联合体
// 共用体 好几个变量共用一块内存空间
////结构体按照变量最大字节申请内存空间
// 共用体中不允许放string 类对象 可以放结构体(结构体中不包含不允许存在变量) 数组
// 共用体的大小 每次申请基础类型(结构体不算)中最大的空间
MyUnion MU;
MU.Num = 100;
cout << sizeof(MU) << endl;
cout << MU.Num << endl;
MU.bSuccess = true;
cout << &MU.Num << " " << &MU.Height << " " << &MU.bSuccess << endl;
cout << sizeof(MyStruct1) << endl;
#pragma endregion
动态数组
动态数组
#pragma region 动态数组
// 不是真正的动态数组
cout << "请输入一个个数:";
int Count = 0;
cin >> Count;
int* Nums = new int[Count] {};
for (int i = 0; i < Count; ++i)
{
cout << Nums[i] << endl;
}
//// 在申请新的内存空间之前一定一定要把老的空间回收掉
delete[] Nums;
Nums = nullptr;
//cout << "============================" << endl;
Nums = new int[Count * 2]{};
for (int i = 0; i < Count * 2; ++i)
{
cout << i << endl;
}
delete[] Nums;
Nums = nullptr;
new int[5]{} int Nums[5] int*
{
auto Humans = new AHuman* [2]{new AHuman(), new AHuman()};
for (int i = 0; i < 2; ++i)
{
delete Humans[i];
Humans[i] = nullptr;
}
delete[] Humans;
Humans = nullptr;
}
#pragma endregion
命名空间
命名空间
#pragma region 命名空间
// C++独有 1)进行功能隔离 2)防止变量重名
using std::cout; // 只引入std下的cout,那么std下的其他变量不引入,其他成员仍然需要加std::
using namespace std; // 引入std命名空间下的所有的内容 cout endl string
cout << "Hello" << std::endl;
cout << UE5::Version << std::endl;
using namespace UE5;
cout << Version << std::endl;
using UE5::Version;
cout << Version << std::endl;
UE5::Editor::Launch();
using namespace UE5;
Editor::Launch();
using UE5::Editor::Launch;
Launch();
#pragma endregion
抽象类
抽象类
// 抽象类可以当作一个接口使用,谁实现这个接口谁就有飞行的能力
class IFly
{
public:
//virtual int Fly1() = 0;
virtual void Fly() = 0; // virtual方法后面 = 0 变成抽象方法(不需要写实现)
};
class Bird : public IFly
{
public:
virtual void Fly() override
{
std::cout << "Bird Fly" << std::endl;
}
};
class AirPlane : public IFly
{
public:
virtual void Fly() override
{
std::cout << "Airplane Fly" << std::endl;
}
};
class ASuperMan : public IFly
{
public:
virtual void Fly() override
{
std::cout << "超人会飞" << std::endl;
}
};
void TestFly(IFly& TempFly)
{
TempFly.Fly();
}
#pragma region 抽象类
// 掌握语法 当作接口去使用
// 使用virtual = 0 修饰的方法属于抽象方法,抽象方法所在的类叫做抽象类,抽象类无法进行实例化(创建对象)
//IFly Fly = IFly();
Bird B;
B.Fly();
AirPlane AP;
AP.Fly();
IFly* TempFly = &B;
TempFly->Fly();
TempFly = &AP;
TempFly->Fly();
TestFly(B);
TestFly(AP);
ASuperMan SM;
TestFly(SM);
#pragma endregion
Day5