CPP
2024-1-26开始学习cpp…
1-26
参考视频:最好的C++教程
P1: Welcome to CPP
CPP
- 编译型语言
- 更高的效率
- 对硬件的直接控制
- 跨平台
P2: How to Setup CPP on Windows
IDE: Visual Studio
集成开发环境IDE
Hello cpp
# include <iostream>
int main()
{
std::cout << "hello cpp" << std::endl;
std::cin.get();
}
P3: How CPP Works
#include
任何以“#”开头的都是预处理指令。
include指的是包含一个文件
另外std表示标准
std::cout << << std::endl输出到控制台,endl表示换行
<<就像printf一样,std::cout.print().print(std::endl)
std::cin.get()等待输入回车。
int main:函数输入输出,输入值和返回值,如果没有指定返回值那么将返回0.
预处理
include
可执行程序
编译
链接
P4
P5
P8: Variables in CPP
程序:数据操作
数据类型的差距只是大小,另外就是格式化输出的时候会有差距。
P9: Functions in CPP
P10: CPP Header Files
P11: How to DEBUG CPP in Visual Studio
P12: CONDITIONS and BRANCHES in CPP
P13: BEST Visual Studio Setup for CPP Projects
filters
VS的虚拟文件目录
show all files
项目实际文件目录
setup projects structures
P14: Loops in CPP
for
一般使用i表示迭代,i也许代表迭代器iterator
for (int i = 0; i < 5; i++)
{
Log("Hello World!");
}
数组遍历
while
游戏循环
do while
P15: Control Flow in CPP
continue
直接进入下一次循环
break
结束循环
return
结束函数
P16: Pointers
指针只是一个地址,一个整数
0 NULL nullptr
* &
new
int main()
{
char* buffer = new char[8];
memset(buffer, 0, 8);
delete[] buffer;
std::cin.get();
}
pointers to pointers
指针也是一个变量,它也有内存地址
指向指针的指针
char** buffer = &buffer;
P17 References in CPP
reference只是指针的扩展,一种语法糖
传值
传址
引用必须初始化,并且没办法更改引用对象
但是可以用指针实现。。。
P18: Classes in CPP
OOP
C++
c
java
c#
面向对象
面向过程
Classes
类是一种将数据和函数组织在一起的方式,本质上是一种自制类型。
想象游戏中的一个玩家角色,需要X和Y的坐标表示位置,可能还需要其他属性比如移动速度,以及可能需要3D模型在屏幕上显示玩家,所有的数据都需要存储到某些地方。
int main()
{
int PlayerX, PlayerY;
int PlayerSpeed = 2;
std::cin.get();
return 0;
}
但是需要创建第二个玩家,第三个玩家的时候需要不断地重复这段代码,这样的源代码是难以维护的,因此考虑使用类进行简化。
就像函数一样,为了简化代码。
实例化和对象
class Player
{
int x, y;
int speed;
};
int main()
{
Player player;
player.x = 5;
}
编译错误:player中的对象无法访问类中的私有成员。
访问控制
当创建一个类是,你可以指定类中属性的可见性,默认情况下,类中的所有成员都是私有的。这意味着,类内部的函数才能访问这些变量。
如果我们需要在main函数中访问类中的变量,需要指定这些变量为公共的public,公有的代表我们可以被允许在类外访问这些变量。
class Player
{
public:
int x, y;
int speed;
};
int main()
{
Player player;
player.x = 5;
}
现在可以在外部访问类中的变量了,main函数的变量访问也将成功。
方法:Method:类的函数
class Player
{
public:
int x, y;
int speed;
};
void Move(Player& player, int xa, int ya)
{
player.x += xa * player.speed;
player.y += yx * player.speed;
}
int main()
{
Player player;
player.x = 5;
}
将上面的函数移动到类中就变成了方法,需要注意的是:当在类中是不再需要传递类中变量的参数而是直接使用。
class Player
{
public:
int x, y;
int speed;
void Move(int xa, int ya)
{
x += xa * speed;
y += yx * .speed;
}
};
int main()
{
Player player;
player.x = 5;
}
类可以让代码变得更加简洁,当处理大量代码,这将会是一个巨大的优势。
也许你可以将变量再次改为私有,通过在外部调用函数函数能够访问私有变量吗?
P19: Classes vs Structs in CPP
类和结构体的区别:非常小的区别,类的变量默认是私有的,结构体则是公有的。
在CPP中结构体存在的唯一原因是cpp需要维持它和c之间的兼容性,因为C中没有类。
那C中的结构体和CPP中的结构体的区别?
C中的结构体可以定义函数吗?可以规定变量的私有或者公有的属性吗?
如果你需要进行兼容。只需要这样既可以
#define struct class
什么时候使用类,什么时候使用结构体?
我想这个问题没有一个确切的答案,因为每个人对于类和结构的定义不同,类是什么,结构体是什么,这是个人的编程风格。
如果讨论一些plain old data pod的时候,使用struct也许比较好.
一个很好的例子是数学中的向量类:
struct Vec2
{
float x, y;
};
这并不是说不在结构体中使用方法,它不想player类一样复杂,需要很多操作。Vec只需要简单的对两个数进行操作,没有啥变化。
继承:继承增加了另外一层复杂性,结构体应该尽量简单。
另外如果结构体B继承了类A,编译器会发出警告,但是不影响运行。
也许你可以在变量是公有的时候使用结构,因为这样可以帮助你省略public?
如果只是代替一个结构中的数据,使用结构体;如果实现有很多功能的类,像游戏世界的玩家,或者其他可以继承的东西,使用类。
额,很多行内的人这样区分;但是技术上这两者处理可见性以外没有什么区别。
C中的结构体
在结构体中使用指向函数的指针,从而实现类似于面向对象编程中的成员函数的功能。这种技术通常被称为函数指针,通过这种方式,你可以将函数与结构体关联起来,实现一些面向对象编程的特性。这在C语言中常用于实现简单的面向对象的思想。
#include <stdio.h>
// 定义一个结构体
struct Person {
char name[50];
int age;
// 声明一个指向函数的指针
void (*displayInfo)(struct Person*);
};
// 定义一个函数,用于显示人员信息
void display(struct Person* person) {
printf("Name: %s, Age: %d\n", person->name, person->age);
}
int main() {
// 创建一个结构体变量
struct Person person1 = {"Alice", 25};
// 将函数指针指向 display 函数
person1.displayInfo = &display;
// 通过函数指针调用 display 函数
person1.displayInfo(&person1);
return 0;
}
P20: How to Write a CPP Class
下面将通过一个简单的日志类实现,演示类的基本用法。
什么是日志类?
日志类是管理日志消息的一种方式,将程序的消息打印到控制台/不同颜色的控制台/输出到文件或者网络。日志消息可以用于调试,在游戏和应用开发中起到帮助。
实现日志类
下面实现错误,警告,消息三个级别的日志类。
# include <iostream>
class Log
{
public:
const int LogLevelError = 0;
const int LogLevelWarning = 1;
const int LogLevelInfo = 2;
private:
int m_LogLevel = LogLevelInfo;
public:
void SetLevel(int level)
{
m_LogLevel = level;
}
void Error(const char* message)
{
if (m_LogLevel >= LogLevelError)
std::cout << "[Error]: " << message << std::endl;
}
void Warn(const char* message)
{
if (m_LogLevel >= LogLevelWarning)
std::cout << "[Warning]: " << message << std::endl;
}
void Info(const char* message)
{
if (m_LogLevel >= LogLevelInfo)
std::cout << "[Info]: " << message << std::endl;
}
};
int main()
{
Log log;
log.SetLevel(log.LogLevelWarning);
log.Warn("Hello");
log.Error("Hello");
log.Info("Hello");
std::cin.get();
}
这只是一个简单的demo,并不意味着一个Log类需要这么写,会在后面介绍生产级别的代码。
P21: Static in CPP
static分为两种,一种是类外,一种是类中。
在link阶段是局部的,也就是只对它的编译单元obj起作用
类中的static变量表示所有类的实例共享,相当于函数多次对一个变量进行操作
举个例子
// Main.cpp
#include <iostream>
int s_variable = 10;
int main()
{
std::cout << s_variable << std::endl;
std::cin.get();
}
// Static.cpp
static int s_Variable = 5;
编译运行之后,结果是main函数输出结果是10,这说明编译单元static.cpp并没有影响到编译单元main.cpp中的变量。
如果将编译单元Static.cpp中的static去掉,那么链接器将会报错,
// Static.cpp
int s_Variable = 5;
外部连接,将static中的变量导入
// Main.cpp
#include <iostream>
extern int s_Variable;
int main()
{
std::cout << s_Variable << std::endl;
std::cin.get();
}
// Static.cpp
int s_Variable = 5;
额,后面函数也是一样。
Static and Private
全局变量可能会造成奇怪的bug,因为其他编译单元可能总是记挂着它,因此可能需要使用static替代。
全局变量,静态全局和局部变量
全局变量:整个程序
静态全局变量:文件
静态局部变量:文件局部
P22: Static for Classes and Structs in CPP
P21讨论了在类外的static关键字的含义,现在将讨论在类中的static的含义。
在几乎所有面向对象的编程语言中,class内部的static意味着在这个类的所有实例中,只存在一个变量的实例。如果创建一个Entity的类,并不断实例化,然而static的变量只有一个。如果一个实例化对象更改了这个变量,那么所有的实例化对象的这个变量都将会更改,就像全局变量一样。
例子
如果类中没有static关键字,那么实例化两个对象,两个对象的x,y是不一样的值,一个属于e,一个属于e1.
#include <iostream>
struct Entity
{
int x, y;
void Print()
{
std::cout << x << ", " << y << std::endl;
}
};
int main()
{
Entity e;
e.x = 2;
e.y = 3;
Entity e1 = { 5, 8 };
e.Print();
e1.Print();
std::cin.get();
}
将x和y修改为static变量,实际上这样x,y已经不在属于类成员,因此在注释的地方初始化会报错,换个写法可以。但是严格来说,这样也是不对的。毕竟x,y实际上就是一个全局变量。
运行程序,结果显示了两次5,8,这意味着实际上两个实例化对象共享同一个x,y。有点像全局变量。
#include <iostream>
struct Entity
{
static int x, y;
void Print()
{
std::cout << x << ", " << y << std::endl;
}
};
int Entity::x;
int Entity::y;
int main()
{
Entity e;
e.x = 2;
e.y = 3;
// Entity e1 = { 5, 8 };
Entity e1;
e1.x = 5;
e1.y = 8;
e.Print();
e1.Print();
std::cin.get();
}
尽管你可以使用全局变量;或者使用一个静态全局变量,它可以在一个编译单元进行链接,而不会在整个项目中都是全局可见的。也许创建一个静态全局变量和类中的静态变量似乎一样,为什么还要在类内部使用static呢?
如果你有一些东西,比如一条需要在所有实例中共享的消息,,那么将这条消息放在类内部是有意义的,因为它是和类相关的。
静态方法无法访问非静态变量
就像这样,静态方法是无法访问非静态变量的。
#include <iostream>
struct Entity
{
int x, y;
static void Print()
{
std::cout << x << ", " << y << std::endl;
}
};
int main()
{
Entity e;
e.x = 2;
e.y = 3;
Entity e1;
e1.x = 5;
e1.y = 8;
Entity::Print();
Entity::Print();
std::cin.get();
}
额,在类中的静态方法属于类本身,而非对象。非静态变量是属于对象的,那么静态变量并不知道调用那么对象的参数。
在类中实现一个方法,其实就是在类外的一个static方法然后传入对象参数。就像这样子:
#include <iostream>
struct Entity
{
int x, y;
};
static void Print(Entity e)
{
std::cout << e.x << ", " << e.y << std::endl;
}
int main()
{
Entity e;
e.x = 2;
e.y = 3;
Entity e1;
e1.x = 5;
e1.y = 8;
Print(e);
Print(e1);
std::cin.get();
}
P23: Local Static in CPP
额,各种地方定义的static变量其实生命周期都是一样的,关键在于它们作用域范围不一样。
#include <iostream>
void Function()
{
// int i = 0;
static int i = 0;
i++;
std::cout << i << std::endl;
}
int main()
{
Function();
Function();
Function();
std::cin.get();
}
如果i不是一个static变量,那么将输出三次“1”,因为每一次进入函数都会初始化一个变量i为0,这样每次程序运行的结果都将一致。那么如果添加static,它就像一个全局变量,只会被初始化一次,并且保持被修改的状态。
就像这样,只不过它的作用域在函数内,不想全局变量一样可以被随意访问。
#include <iostream>
int i;
void Function()
{
i++;
std::cout << i << std::endl;
}
int main()
{
Function();
// 唯一的区别就是你可以在函数以外的地方更改i的值
i++;
Function();
Function();
std::cin.get();
}
local static可以保留全局变量生命周期又可以限制访问范围。
P24: Enums in CPP
枚举
#include <iostream>
enum Example : unsigned char
{
A = 5, B, C
};
int a = 0;
int b = 1;
int c = 2;
int main()
{
int Value = B;
if (Value == B)
{
// Do something here
}
std::cin.get();
}
P25: Constructors in CPP
构造函数是一种特殊的方法,它会在类每次实例化的时候运行
举个例子
#include <iostream>
class Entity
{
public:
float x, y;
void Print()
{
std::cout << x << ", " << y << std::endl;
}
};
int main()
{
Entity e;
// std::cout << e.x << std::endl;
e.Print();
std::cin.get();
}
看起来似乎打印了一些随机的浮点数值,这是因为实例化Entity类的时候,只分配了内存,但是还没有对内存进行初始化,这样它就会显示内存原来的值。
需要做的是可能需要将初始化内存并把它设置为0.
Init函数
#include <iostream>
class Entity
{
public:
float x, y;
void Init()
{
x = 0.0f;
y = 0.0f;
}
void Print()
{
std::cout << x << ", " << y << std::endl;
}
};
int main()
{
Entity e;
// e.Init();
std::cout << e.x << std::endl;
e.Print();
std::cin.get();
}
虽然Init函数能够帮助我们初始化类的变量,但是每次实例化一个对象都需要显示调用Init函数。构造函数能在每次实例化的时候自动调用,简化了这一过程。
初始化:构造函数
java中可以自动化初始化基本数据类型,但是cpp不会。
如果写构造函数,那么它就是一个空的构造函数。
#include <iostream>
class Entity
{
public:
float x, y;
Entity()
{
x = 0.0f;
y = 0.0f;
}
void Print()
{
std::cout << x << ", " << y << std::endl;
}
};
int main()
{
Entity e;
// e.Init();
std::cout << e.x << std::endl;
e.Print();
std::cin.get();
}
函数重载
有相同函数名,但是参数不一样的函数版本。
#include <iostream>
class Entity
{
public:
float x, y;
Entity()
{
x = 0.0f;
y = 0.0f;
}
Entity(float x1, float y1)
{
x = x1;
y = y1;
}
void Print()
{
std::cout << x << ", " << y << std::endl;
}
};
int main()
{
// Entity e;
Entity e(10.0f, 5.0f);
std::cout << e.x << std::endl;
e.Print();
std::cin.get();
}
构造函数只会在实例化的时候运行,因此,如果使用静态方法,那么构造函数不会执行。
P26: 析构函数
构造函数是当实例化一个对象时运行,析构函数是当对象销毁时运行。
析构函数是你卸载变量等东西并清理使用的内存。析构函数同时适用于栈和堆分配的对象。
当new一个对象,如果调用delete析构函数会被调用。
如果只是栈上的一个变量,当生命周期结束,析构函数也会被调用。
#include <iostream>
class Entity
{
public:
float x, y;
Entity()
{
std::cout << "Created Entity!" << std::endl;
x = 0.0f;
y = 0.0f;
}
~Entity()
{
std::cout << "Destroryed Entity!" << std::endl;
}
void Print()
{
std::cout << x << ", " << y << std::endl;
}
};
void Function()
{
Entity e;
e.Print();
}
int main()
{
Function();
std::cin.get();
}
P27: 继承
面向对象是一个巨大的编程范式,类和继承是它的两个基本特性,也是可以利用的最强大的特性。
继承允许创建相关联的类的层次结构。换句话说,它允许我们包含公共功能的基类,他允许在基类(父类)的基础之上创建子类。
如果没有继承的思想,那么代码将会得到相当的重复
#include <iostream>
class Entity
{
public:
float X, Y;
void Move(float xa, float ya)
{
X += xa;
Y += ya;
}
};
class Player
{
public:
const char* Name;
float X, Y;
void Move(float xa, float ya)
{
X += xa;
Y += ya;
}
void PrintName()
{
std::cout << Name << std::endl;
}
};
int main()
{
std::cin.get();
}
如果使用继承,那么将会得到简洁的版本。
代码在X86 和X64下有不同的运行结果。
#include <iostream>
class Entity
{
public:
float X, Y;
void Move(float xa, float ya)
{
X += xa;
Y += ya;
}
};
class Player : public Entity
{
public:
const char* Name;
void PrintName()
{
std::cout << Name << std::endl;
}
};
int main()
{
std::cout << sizeof(Entity) << std::endl;
std::cout << sizeof(Player) << std::endl;
std::cin.get();
}
P28:虚函数
虚函数允许在子类中重写方法,例如一个子类重写父类中的方法。
P29: 接口 纯虚函数
纯虚函数允许在基类中定义一个没有实现的函数,然后强制子类去实现该函数。
#include <iostream>
#include <string>
class Entity
{
public:
virtual std::string GetName() = 0;
};
class Player : public Entity
{
private:
std::string m_Name;
public:
Player(const std::string& name)
: m_Name(name){}
std::string GetName() override { return m_Name; }
};
void PrintName(Entity* entity)
{
std::cout << entity->GetName() << std::endl;
}
int main()
{
// Entity* e = new Entity();
Entity* e = new Player("123");
PrintName(e);
Player* p = new Player("Cherno");
PrintName(p);
}
P30: 可见性
可见性是一个属于面向对象的编程理念,它指类的某些成员或方法实际上有很多可见性。
可见性对实际的程序运行方式没有影响。对程序性能或类似的东西也没有影响。它只是语言中存在的东西,帮助你写更好的组织代码。
private,protected,public
class默认私有,struct默认公有。
#include <iostream>
#include <string>
class Entity
{
private:
int X, Y;
public:
Entity()
{
X = 0;
Y = 0;
}
};
class Player : public Entity
{
public:
Player()
{
X = 2;
}
};
int main()
{
Entity e;
e.X = 2;
std::cin.get();
}
P31: 数组
指针基本上是数组的工作方式。
数组基本上是元素的集合,按特定顺序排列的一堆东西。它方便批量化处理大量数据。
数组越界
#include <iostream>
int main()
{
int example[5];
example[0] = 2;
example[4] = 4;
// Debug模式会报错,但是Release模式就会发生内存违规访问。
example[-1] = 5;
example[5] = 2;
std::cout << example[0] << std::endl;
std::cout << example << std::endl;
std::cin.get();
}
数组赋值
#include <iostream>
int main()
{
int example[5];
//example[0] = 2;
//example[1] = 2;
//example[2] = 2;
//example[3] = 2;
//example[4] = 4;
for (int i = 0; i < 5; i++)
example[i] = 2;
std::cout << example[0] << std::endl;
std::cout << example << std::endl;
std::cin.get();
}
数组和指针
#include <iostream>
int main()
{
int example[5];
example[2] = 5;
std::cout << example[2] << std::endl;
int* ptr = example;
*(ptr + 2) = 6;
//*((int*)ptr +2) = 6;
//*(int*)((char*)ptr +8) = 6;
std::cout << example[2] << std::endl;
std::cin.get();
}
栈和堆上创建数组
如果你在函数中创建一个数组,需要返回它那么需要使用new关键字或者传入数组的地址。
在堆上创建数组,你可以控制它的生命周期,创建或者销毁。
#include <iostream>
class Entity
{
public:
//int* example = new int[5];
int example[5];
Entity()
{
for (int i = 0; i < 5; i++)
example[i] = 2;
}
};
int main()
{
Entity e;
std::cin.get();
}
安全数组std::array
#include <iostream>
#include <array>
class Entity
{
public:
// 原始数组
//int example[5];
std::array<int, 5> example;
Entity()
{
for (int i = 0; i < example.size(); i++)
example[i] = 2;
}
};
int main()
{
Entity e;
std::cin.get();
}
P32:字符串
字符串就是字符数组
额,烫烫烫是数组守卫,栈守卫。就是那些预分配的位置cc
#include <iostream>
int main()
{
// 不能更改字符
const char* name = "liang";
std::cout << name << std::endl;
// char name2[5] = { 'l', 'i', 'a', 'n', 'g' };
char name2[6] = { 'l', 'i', 'a', 'n', 'g', '\0'};
std::cout << name2 << std::endl;
std::cin.get();
}
std::string
string是cpp的类,有一些可使用的方法。
它接收一个const char*的参数
#include <iostream>
#include <string>
int main()
{
//这里接收char*参数,所以不能把指针加在一起
std::string name = "liang";// + " hello";
// name是有方法的因此可以使用+
name += " hello";
std::cout << name << std::endl;
std::cin.get();
}
加一个引用可以避免字符串的复制,因为这很花费时间。
#include <iostream>
#include <string>
//承诺不会修改
void PrintString(const std::string& string)
{
std::cout << string << std::endl;
}
int main()
{
//这里接收char*参数,所以不能把指针加在一起
std::string name = "liang";// + " hello";
// name是有方法的因此可以使用+
name += " hello";
std::cout << name << std::endl;
std::cin.get();
}
P33:字符串字面量
#include <iostream>
#include <string>
int main()
{
//const char* name = u8"L\0ang";
const char* name = "Lang";
const wchar_t* name2 = L"Lang";
const char16_t* name3 = u"Lang";
const char32_t* name4 = U"Lang";
std::cout << strlen(name) << std::endl;
std::cin.get();
}
P34:Const
const伪关键字,因为他在代码方面做不了什么。
有点像类的可见性,只是一个机制,让代码更加干净。
#include <iostream>
#include <string>
int main()
{
const int MAX_AGE = 90;
int a = 5;
//const int a = 5;
a = 2;
std::cin.get();
}
const int*和int const*实际是完全一样的,只是你看见不同的编程风格,重要的是这是一个承诺不会改变指向的指针。
#include <iostream>
#include <string>
int main()
{
const int MAX_AGE = 90;
const int* a = new int;
//int const* a = new int;
int* a = new int;
*a = 2;
a = (int*)&MAX_AGE;
std::cout << a << std::endl;
std::cin.get();
}
但是如果int* const和int const*那么将会有很大的区别。
#include <iostream>
#include <string>
int main()
{
const int MAX_AGE = 90;
// const int* const a = new int;
int* const a = new int;
//int const* a = new int;
*a = 2;
a = (int*)&MAX_AGE;
std::cout << a << std::endl;
std::cin.get();
}
如果你个方法标记为const,那么它承诺不会修改函数中的变量,因此只能调用const方法。所以优势后Getter会有const和非const的两个版本。
另外被mutable标记的变量可以被const方法修改。
#include <iostream>
#include <string>
class Entity
{
private:
int m_X, m_Y;
mutable int var;
public:
int GetX() const
{
//m_X = 2;
var = 2;
return m_X;
}
void SetX(int x)
{
m_X = x;
}
};
void PrintEntity(const Entity& e)
{
std::cout << e.GetX() << std::endl;
}
int main()
{
Entity e;
std::cin.get();
}
P35:Mutable
mutable主要有两个应用,和const,lambda
主要的应用类的const
#include <iostream>
#include <string>
class Entity
{
private:
std::string m_Name;
mutable int m_DebugCount = 0;
public:
const std::string& GetName() const
{
m_DebugCount++;
return m_Name;
}
};
int main()
{
const Entity e;
e.GetName();
std::cin.get();
}
lambda等于“=”的时候是传值,额使用mutable让代码可以干净一点,但实际上lambda中的x不是和外面的x一样的。
#include <iostream>
#include <string>
class Entity
{
private:
std::string m_Name;
mutable int m_DebugCount = 0;
public:
const std::string& GetName() const
{
m_DebugCount++;
return m_Name;
}
};
int main()
{
const Entity e;
e.GetName();
int x = 8;
auto f = [=]() mutable
{
// y = x;
// y++;
x++;
std::cout << x << std::endl;
};
f();
// x = 8;
std::cin.get();
}
P36:初始化列表
相当于一种语法糖,帮助按照定义变量的顺序在构造函数初始化对象。
P37:三元操作符
三元操作符实际上是if语句的语法糖。
三元运算符可以在定义的时候初始化更快。if也可以做,但是没那么干净,另外三元运算符可以嵌套,但是代码可读性会很差。
#include <iostream>
#include <string>
static int s_Level = 1;
static int s_Speed = 2;
int main()
{
if (s_Level > 5)
s_Speed = 10;
else
s_Speed = 5;
s_Speed = s_Level > 5 ? 10 : 5;
std::string rank = s_Level > 10 ? "Master" : "Beginner";
std::string otherRank;
if (s_Level > 10)
otherRank = "Master";
else
otherRank = "Beginner";
std::cin.get();
}
P38:创建并初始化CPP对象
基本上创建一个类需要对其进行实例化(除非完全是一个静态类)
即使创建一个空的类,也会至少在内存中占用一个字节。
内存区域:堆,栈,代码区(一些机器码)
栈
生命周期结束,就是代码不在需要引用这个对象,那么将会被销毁。在一个函数里面就是调用的函数被返回,函数里面的栈对象就被销毁了。
堆手动创建,手动销毁。
#include <iostream>
#include <string>
// define
using String = std::string;
class Entity
{
private:
String m_Name;
public:
Entity() : m_Name("Unknown"){}
Entity(const String& name) : m_Name(name){}
const String& GetName() const { return m_Name; }
};
int main()
{
//Entity entity;
Entity entity = Entity("Lang");
std::cout << entity.GetName() << std::endl;
std::cin.get();
}
可以任何时候这样实例化一个对象,但是如果需要在函数的生命周期以外使用对象,那么这样是不行的。
就像这样
#include <iostream>
#include <string>
// define
using String = std::string;
class Entity
{
private:
String m_Name;
public:
Entity() : m_Name("Unknown"){}
Entity(const String& name) : m_Name(name){}
const String& GetName() const { return m_Name; }
};
void Function()
{
int a = 2;
Entity entity = Entity("Lang");
}
int main()
{
Entity* e;
{
Entity entity("Lang");
e = &entity;
std::cout << entity.GetName() << std::endl;
}
std::cin.get();
}
另外如果对象太多或者韩勇非常多的内存,由于栈只有MB级别的大小,因此也会考虑使用堆。
另外,在堆上创建对象将会更加耗时,并且要手动销毁对象。然后使用new关键字返回的是一个对象指针,你要用“->”去调用方法,而不是“.”,因为需要指向。
#include <iostream>
#include <string>
// define
using String = std::string;
class Entity
{
private:
String m_Name;
public:
Entity() : m_Name("Unknown"){}
Entity(const String& name) : m_Name(name){}
const String& GetName() const { return m_Name; }
};
int main()
{
Entity* e;
{
Entity* entity = new Entity("Lang");
e = entity;
std::cout << entity->GetName() << std::endl;
// delete entity;
}
delete e;
std::cin.get();
}