|
虚幻的编译实在是太久了,等待编译太折磨人了
趁代码编译就写点东西吧,不知道能不能帮助到别人,就当对自己的知识点做梳理了吧
第一篇就先梳理一下进入虚幻引擎必须掌握的C++基础知识吧
1.空指针/野指针
空指针
空指针的定义:指针变量指向内存中编号为0的空间
空指针是用来初始化指针变量的
指针指向的内存是不可访问的
野指针
他的定义是:指针变量指向非法的内存空间
2.指针和函数
利用指针作函数参数,可以修改实参的值
#include <iostream>
using namespace std;
int temp = 100;
void testFun(int* a)
{
*a = temp;
}
int main(int argc, char *argv[]) {
int a = 0;
int* pa = &a;
testFun(pa);
cout << a << endl;
}
指针分为两种,一种是原始指针,一种是智能指针,要使用智能指针,需要添加头文件 #include<memory>
3.内存四个区模型
内存分为四个区,分别是堆/栈/全区/代码起,
也有五个区的说法 栈/堆/全局/静态及常量/代码区
4.指针/引用
指针
变量类型* 指针名 = 变量类型 变量
引用
&别名 = 原名 两者的区别
指针是变量/引用是给变量起别名
指针可以为空/引用必须初始化
指针可以修改指向/引用不可改变
指针可以多级/引用只有一级 5.指针函数/函数指针
指针函数
返回一个指针的函数
int (*f)(int x,int y)
函数指针
指向一个函数的指针
int* sub (int x,int y) 6.构造函数
拷贝构造函数:
是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象
类自动生成的三个成员
当我们创建一个类的时候,会自动生成三个成员,分别是,默认构造函数(无参,函数体为空)、默认析构函数(无参,函数体为空)、默认拷贝构造函数,对属性进行值拷贝
构造函数的三种调用方式
构造函数的三种调用方式,括号法、显示法、隐式转换法
括号法
Person p1;
Person p2(10);
Person p3(p2);显示法
Person p1;
Person p2 = Person(10);
Person p3 = Person(p2);隐式转换法
Person p4 = 10; //相当于 Person p4 = Person(10);
Person p5 = p4;深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝操作,浅拷贝是编辑器自动生成的拷贝构造函数
深拷贝:在堆区重新申请空间,进行拷贝操作
浅拷贝存在问题:
析构时堆区的内存重复释放
用深拷贝来解决
Person (const Person &p)
{
m_age = p.m_age;
m_Height = new int(*p.m_Height);
}7.静态成员
概念:所有对象共享同一份数据,类似游戏跨服的概念
静态成员变量:所有对象共享同一份数据,在编译阶段分配内存
类内生命类外初始化
static int age;
int person::age = 100;8.this
概念:this是一个指针,隐藏在每一个非静态成员函数中
两个常见用途:用来解决命名冲突、允许成员函数返回该对象
9.常函数
概念:被const修饰的函数
常函数内不可修改成员属性
可使用关键字mutable解除限制 常对象
声明对象前加const
常对象只能调用常函数
10.友元
全局函数做友元
把全局函数放到类里并添加friend关键字,全局函数即可访问私有成员
friend void ReadPublicAndPrivate(room* pRoom);类做友元
把访问类放到被访问类里并加friend关键字
friend class ClassName;成员函数做友元
告诉编辑器某个类下的某个函数可以访问我的私有成员
friend void 类名::函数名()11.运算符重载
概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
关键字:operator
两种重载的方法:成员函数重载、全局函数重载
成员函数重载
Person operator+ (Person &p) //行参用指针或引用避免拷贝节省空间
{
Person temp;
temp.m_a = this->m_a+p.m_a;
return temp
}
Person p3 = p1.operator+(p2);//本质调用
Person p3 = p1+p2; //两种写法等同全局函数重载
Person operator+ (Person &p1,Person &p2)
{
Person temp;
temp.m_a = p1.m_a + p2.m_a;
return temp;
}
Person p3 = operator+(p2,p1);//本质调用
Person p3 = p1+p2; //两种写法等同12.继承
继承中的对象模型
父类中所有的非静态成员都会继承下来在子类中保存一份
包括私有变量,会被编辑器自动隐藏且不能访问
可使用 sizeof() 可以查看派生类大小
子类访问同名成员
默认访问子类自身成员
son.a;
son.fun();访问子类的父类方法
son.father::a;
son.father::fun();通过子类类名访问父类的静态成员
son::father::a;菱形继承
也叫钻石继承,会产生二义性,拥有两份数据,会造成资源浪费
我么可以利用虚继承 (virtual关键字) 来保证数据唯一
class son : virtual public father{};son为虚基类虚继承了father,son类会产生一个虚基类指针来保证数据唯一
13.多态
静态多态
地址早绑定,编译器编译时就知道调用哪个函数了比如函数重载运算符重载
动态多态
地址晚绑定,子类重写父类的虚函数,使用时利用父类的指针或引用来指向子类对象
虚函数
函数前加 virtual 关键字,自动生成虚函数指针(vfptr)并指向虚函数表(vftable)
子类重写虚函数时子类的虚函数表内部会替换成子类的虚函数地址
纯虚函数
语法:virtual 返回值类型 函数名(参数列表) = 0;
类中有了纯虚函数,这个类称为抽象类,抽象类无法实例化对象,
子类必须重写抽象类中的纯虚函数,否则也属于抽象类
虚析构和纯虚析构
虚析构:抽象类的析构函数前加 virtual关键字,可以解决父类指针释放子类对象时堆区数据释放不干净问题
纯虚析构函数:
virtual ~animal()=0;
animal::~animal()
{};14.文件操作
头文件<fstream>
文件类型分为两种 文本文件、二进制文件
操作文件的三大类: ofstream(写操作) ifstream(读操作) fstream(读写操作)
5个步骤
包含头文件
#include<fstream>创建流对象
ofstream ofs;打开文件
ofs.open(“文件路径”,打开方式);
ios::in //为读文件而打开文件
ios::out //为写文件而打开文件
ios::ate //初始位置:文件尾
ios::app //追加方式写文件
ios::trunc //如果文件存在先删除,在创建
ios::binary //二进制方式写数据
ofs<<&#34;写入的数据&#34;;关闭文件
ofs.close();15.模版
函数模版
template<typename T>使用方法:
mySwap (5 , 4.9); //自动类型推导
mySwap<int>(a,b); //显示类型推模版函数与普通函数的区别
1.普通函数可以隐式类型转换
2.模版函数自动类型推导不可以隐式类型转换
3.模版函数显示指定类型可以发生隐式类型转 类模版
template<class T> 类模版只能用显示指定类型不能用自动类型推导
类模版可以有默认参数
template<class NameType,class AgeType = int 实例化的对象向函数传参的方式:指定传入类型、参数模版化、整个类模版化
STL
六大组件:容器、算法、迭代器、仿函数、适配器、空间适配器
容器
容器分为序列式容器和关联式容器
常用的数据结构:数组/链表/树/栈/队列/集合/映射表
算法
质变算法:拷贝/替换/删除
非质变算法:查找/计数/遍历/寻找极值
迭代器
概念:迭代器式容器和算法之间的粘合剂,每个容器都有自己专属的迭代器,类似指针
16.智能指针
shared_ptr
共享指针,记录有多少指针指向了这块内存,当数量为0时自动释放
共享指针允许多个指针指向一块地址
头文件: #include<memory>声明和初始化语法
shared_ptr<int> p {new int(100)};
shared_ptr<int> p {make_shared<int>(100)};
Ball* rp = p.get(); //获取一个裸指针unique_ptr
独占指针,仅自己可以指向对应数据,用来指向唯一数据,数据是唯一的不可以copy
weak_ptr
是unique_ptr的补充
auto_ptr
已经被废弃
这是我梳理的关于C++的知识点,这也是进入虚幻引擎的必要条件,写到这里,代码也终于快编译完了。
这个专栏主要用来消磨编译时间的,还没想好后边写啥,可能会写点引擎源码方面的东西 |
|