一
指针
内容在C中已经熟悉
- 指针本身为一个代表地址的整数
- 指针的
*
运算符通常被称为dereference运算符,可逆引用指针。
引用
引用必须引用已经存在的变量,其本身不是变量也不占用内存,相当于变量的别名
使用 int&
类此的形式声明引用变量
其作用为向函数传递变量,而不是只传递值,这使得函数可以直接对变量进行操作。可以达到与使用指针相似的效果,但比使用直至简单。
例如用指针实现
#include<iostream>
void Increment(int* value)
{
(*value)++;
}
int main(){
int a = 5;
Increment(&a);
std::cout<<a<<std::endl;
std::cin.get();
}
引用实现
#include<iostream>
void Increment(int& value)
{
value++;
}
int main(){
int a = 5;
Increment(a);
std::cout<<a<<std::endl;
std::cin.get();
}
- 声明一个引用时必须对其赋值且引用的内容不可改变
C++类(class)
类是对数据和功能组合在一起的一种方法。例如角色在游戏中的位置,角色可能拥有的某些属性等。直接创建这些内容会十分麻烦和混乱。
通过使用类可以简化这些操作,我们可以创建一个叫做Player的类,它包含了所有的数据,当然,类中也可以包含函数,被称为方法
由类类型构成的变量成为对象,新的对象变量称为实例
默认情况下,一个类中所有的东西都是私有的,这意味着只有类中的函数才能访问这些变量。我们希望从main函数中访问这些变量,需要将其设置为public
class Player
{
public:
int x,y;
int speed;
void Move(int xa, int ya)
{
//此处的X,Y,speed指的都是当前对象的变量
x += xa * speed;
y += ya * speed;
}
};
int main()
{
Player player;//实例化了一个Player对象
player.move(1,-1);//player对象内的函数
std::cin.get();
}
总的来说,类允许我们将变量分组到一个类型中,并为这些变量添加功能
类可以让代码变得简洁,使其容易维护。但是不用类搞不定的事情,用类也一样搞不定。类没有提供任何新功能
- 结构体和类的对比
默认情况下,类是
private
的,而结构体是public
的。
如何写一个类:
///
class Log
{
public:
const int LogLevelError = 0;
const int LogLevelWaring = 1;
const int LogLevelInfo = 2;
private:
int m_LogLevel = LogLevelInfo;
public:
void SetLevel(int level)
{
m_messahe = level;
}
void Warn(const char* mess){
if (m_LogLevel >= LogLevelWarn)
std::cout << "[WARING]:"<< message <<std::endl
}
void Error(const char* mess){
if (m_LogLevel >= LogLevelRrror)
std::cout << "[ERROR]:"<< message <<std::endl
}
void Info(const char* mess){
if (m_LogLevel >= LogLevelInfo)
std::cout << "[INFO]:"<< message <<std::endl
}
};
int main(){
Log log;
log.Setlevel(log.LogLevelWaring);
log.Warn("Hello")
std::cin.get();
}
C++中的static(静态)
static有两种意思,这取决于上下文。
其中之一是在类或结构体外部使用static关键字,另一种是在类或结构体内部使用ststic
类外面的static,意味着你声明为static的符号,链接将只是在内部,这意味着它只能对你定义它的翻译单元可见
然而,类或结构体内部的静态变量(static)意味着该变量实际上将与类的所有实例共享内存,这意味着该静态变量在你创建的所有实例中,静态变量只有一个实例,类似的事情也适用于类中的静态方法。在类中,没有实例会传递给该方法。
在这里先讨论类或结构体外的静态
static int s_Variable = 5;
例如此处,静态变量或函数
意味着,当需要将这些函数或变量与实际定义的符号链接时,链接器不会再这个翻译单元的作用域之外,寻找那个符号定义
下面是类或结构体中的静态(static)
在几乎所有面向对象的语言中,静态在一个类中意味着特定的东西,如果把它和变量一起使用,这意味着在类的所有实例中,这个变量只有一个实例
如果我创建一个名为Entity的类,我不断创建ENtity实例,我仍然只会得到那个变量的一个版本。意思是,如果某个实例改变了这个静态变量他会在所有实例中反映这个变化。因此,通过类实例来引用静态变量是没有意义的。因为这就像类的全局实例。静态方法也是一样,无法访问类的实例。静态方法可以被调用,不需要通过类的实例。
而在静态方法内部,你不能写引用到实例的代码
,因为你不能引用到类的实例
C++中的局部静态(local static)
这是在一个局部的作用域。你可以在局部的作用域中使用static来声明一个变量。
声明一个变量,我们需要考虑两种情况,这就是变量的生存期和作用域。
生存期指的是变量的实际存在时间
变量的作用域是指我们可以访问变量的范围
如果在函数的内部声明一个变量,我们不能在其他的函数中访问它。
静态局部(local static)变量允许我们声明一个变量,他的生存期基本上相当于整个程序的生存期
,然而它的作用范围被限制在这个函数内,但他和函数没有什么关系。(你可以在任何作用域中生命这个)
C++枚举(enum)
枚举是给一个值命名的一种方法,所以我们不用一堆叫做a、b、c的整数,我们可以有一个枚举数,它的值是a,b,c与整数相对应
他还能帮助我们,将一组数值集合作为类型,而不仅仅是用整型作为类型
例:
enum Example : unsigned char//冒号及其后内容可选,表示是数据类型,但必须为整数,例如可为char
{
A,B,C//若不赋值,将从0开始递增
}
int main()
{
Example value = B;//此处Example类型变量的值只能为A、B、C中的一种
if(value == 1)
{
//Do something here
}
std::cin.get();
}
在3.内容中的如何写一个类可以用枚举来修改:
class Log{
public:
enum Level{
Error,Warning,Info
}
}
显然会让代码变得简介
同时如果枚举类型变量在赋值时被赋予了枚举以外的内容,编译器将会提醒
构造函数
(此处的构造不是动词)
构造函数基本上是一种特殊类型的方法,它在每次实例化对象时运行
主要的作用是初始化该类
eg:假设我们想要创建一个Entity类
class Entity
{
public:
float X,Y;
void Init(){
X = 0.0f;
Y = 0.0f;
}
void Printf(){
std::cout << X <<","<<Y<<std::endl;
}
}
int main(){
Entity e;
e.Printf();
std::cin.get();
}
这样的话,每次新实例化一个变量就要用Init方法初始化一遍,很繁琐
而构造函数是一种每次你构造一个对象都会调用的方法,像定义其他方法一样定义它,但它没有返回类型,并且它的名称必须与类的名称相同
Entity (){
X = 0.0f;
Y = 0.0f;
}
如果不指定构造函数,仍然会有一个叫做默认构造函数的东西,只不过什么都都不做
带参数的构造函数
其实可以写很多的构造函数,前提是他们由不同的参数,和写一些同名函数是一样的*(函数重载)
函数重载:即有相同的函数(方法)名,但是有不同的参数的不同版本
Entity(float x,float y){
X = x;
Y = y;
}
可以使用下面的方法传入参数
Entity e(10.0f,5.0f);
构造函数,如果不实例化对象,将不会运行,所以如果只使用一个类的静态方法,它不会运行
静态成员是类级别的,也就是它和类的地位等同,而普通成员是对象(实例)级别的.类级别的成员,应先于该类任何对象的存在而存在,所以类的静态成员会被该类所有的对象共享,因此不能在静态方法里面访问非静态元素但非静态方法可以访问类的静态成员及非静态成员
也有一些方法可以删除构造函数,例如下面的例子中,只想让人们用Log::Write()使用Log类,不希望创建实例
有两种方法
- 通过设置为private来隐藏构造函数(默认构造函数)
- 删除构造函数如
Log() = delete;
class Log{
public:
static void Write(){
}
}
int main(){
//
}
析构函数
析构函数与构造函数很相似,析构函数实在销毁对象时运行,任何时候,一个对象将被销毁时,析构函数将被调用它不会返回任何值,也不能带有任何参数
构造函数通常是设置变量或者做任何你需要做的初始化,同样的,析构函数是你卸载变量等东西,并清理你使用过的内存(关闭文件)
构析函数同时适用于栈和堆分配的对象,如果你使用new分配一个对象,当你调用delete时,构析函数会被调用。而如果只是一个栈对象,当作用域结束时,栈对象将被删除,这时,析构函数也会被调用
构析函数写为~,然后是类的名称
~Entity(){
//下面只是示例内容
std::count<<"Destoryed Entity!"<<std:endl;//销毁时打印对应内容
}
析构函数也可以手动调用例如e.~Entity();
C++继承
继承允许我们有一个相互关联的类的层次结构,即它允许我们有一个包含公共功能的基类,然后它允许我们从那个(基)类中分离出来,从最初的父类中创建子类
这些类、继承等如此有用的原因是它可以帮助我们避免代码重复,我们可以把类之间所有的公共功能放在一个父类中,然后从基类(父类)创建(派生)一些类,稍微改变下功能,或者引入全新的功能
看在代码中的应用
//假设有一个Entity类,他将管理游戏中所有实体对象
class Entity{
//例如,每个实体在我们的游戏中都有自己的位置
public:
float X,Y;
//赋予每个实体移动的能力
void Move(float xa, float ya) {
X += xa;
Y += ya;
}
//至此,创建了一个和积累Entity,在游戏中的每个实体都将具有这些特征
}
//下面创建一个新的类Player
//我们可以扩展这个Entity实体类,来创建一个名为Player的新类型,然后让他储存新数据
class Player{
public:
const char* Name;
float X,Y;
void Move(float xa, float ya) {
X += xa;
Y += ya;
}
void PrintName(){
std:<<Name<<std::endl;
}
}
现在把Player变成Entity的子类:在类型声明后写一个冒号,然后写public Entity
class Player : public Entity
```
Player类现在不仅拥有Player类型,而且它也有Entity类型
Player现在拥有Entity拥有的所有东西。任何在Entity中```不是私有```的东西,都可以被Player访问,比如X和Y.所以可以把Player中重复的代码都去掉
``` C++
class Player : public Entity{
const char* Name;
void PrintName(){
std:<<Name<<std::endl;
}
}
可以有下面的用法
int main(){
Player player;
player.PrintfName();
player.Move(5,5);
player.X = 2;
}
其实我们可以应用到概念是叫做"多态",多态是一个单一类型,但是有多个类型的意思.
Player包含Emtity所有的东西再多一些,甚至可以不加这些多的东西,与Entity完全一样,但Player总是Entity的超集(父类是子类的子集)
总的来说,继承是我们扩展现有类并为基类提供新功能的一种方式
补充:C++有三种继承方法,public,protected,private
继承方式 | 基类的public成员 | 基类的protected成员 | 基类的private成员 |
---|---|---|---|
public继承 | 仍为public成员 | 仍为protected成员 | 不可访问 |
protected继承 | 变为protected成员 | 变为protected成员 | 不可访问 |
private继承 | 变为private成员 | 变为private成员 | 不可访问 |
虚函数
虚函数允许我们在子类中重写方法。假设我们有两个类A和B ,B是A派生出来的,即B是A的子类。如果我们在A中创建一个方法,标记为virtual,我们可以选择在B类中重写那个方法,让它做其他的事情
接口(纯虚函数)
纯虚函数允许我们在基类中定义一个没有实现的函数,然后在派生类中实现它。这样,我们就可以在基类中使用这个函数,但是我们不知道它是如何实现的,这就是接口
virtual std::string GetName() = 0;
可见性
可见性指的是对于类的某些成员或方法,谁能看到他们,谁能调用他们,谁能使用它们。
可见性是对程序实际运行方式完全没有影响的东西,对程序性能或类似的东西也没有影响。
C++中有三个基础的可见性修饰符private,protected,public
在其他语言有其他的关键字
private
private成员只能(*特殊:friends 友元)被类的成员函数访问,不能被类的外部访问,也不能被派生类访问
protected
protected比private更可见,比public更不可见
protected指的是这个类和其所有子类都可以访问
public
意味着所有人都可以访问它,包括在main函数的内部
关于可见性的作用
首先,public公开一切纯粹是一个糟糕的想法,对于开发者和写代码而言这是风格问题,是如何写好代码的问题。可见性让代码更加容易维护,容易理解,不管是阅读代码还是扩展代码,与性能无关