C++(一)
#include <iostream> #include "Log.h" #define LOG(x) std::cout << x << std::endl class Player { public: int x, y; int speed; void Move(int xa, int ya) { x += xa * speed; y += ya * speed; } }; int main() { Player player; player.Move(1, -1); std::cin.get(); }
19.CLASSES vs STRUCTS in C++
默认情况下,类是私有的。
struct 默认是公有的,
如果只是想要代替一个结构体中的一些数据,我会使用struct。如果有一个很多功能的类。
20.How to Write a C++ Class
在函数内部写代码时,这样引用了成员变量。在类代码中,哪些是成员变量,哪些是局部变量。
#include <iostream> #define LOG(x) std::cout << x << std::endl 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(); }
![]()
21.Static in C++
#include <iostream>
int s_Variable = 10;
int main()
{
std::cout << s_Variable << std::endl;
std::cin.get();
}
static int s_Variable = 5;
如果删除关键字 static ,得到链接错误。
static:静态。类似于在类中声明一个私有变量。其他所有的翻译单元都不能看到这个s_Variable变量。
修改方法:
1.修改这个变量的实际指向,让它指的是这个
#include <iostream> extern int s_Variable; int main() { std::cout << s_Variable << std::endl; std::cin.get(); }
int s_Variable = 5;
C++静态的的全部含义,当你在类和结构体之外使用静态时。意味着当你声明静态函数或静态变量时,它只会在它被声明的C++文件中被看到。
如果你不需要变量时全局变量,你就需要尽可能多地使用静态变量。
22.Static for Classes and Structs in C++
静态意味着特定的东西。把它和变量一起使用,这意味着在类的所有实例中,这个变量只有一个实例。
静态方法可以被调用,不需要通过类的实例。而在静态方法内部,你不能写引用到类实例的代码。
#include <iostream> struct Entity { int x, y; void Print() { std::cout << x << "," << y <<std::endl; } }; int main() { Entity e; e.x = 2; e.y = 10; Entity e1 = { 3, 8 }; e.Print(); e1.Print(); std::cin.get(); }
运行结果
2,10
3,8
#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;
Entity::x = 2;
Entity::y = 10;
Entity e1;
Entity::x = 5;
Entity::y = 8;
e.Print();
e1.Print();
std::cin.get();
}
运行结果:
5,8
5,8
因为是在修改相同的变量
当你想要跨类使用跨类变量时,只需要创建一个全局变量,或者不使用全局变量,而是使用一个静态全局变量,它是在内部进行链接的,它不会在你的整个项目中是全局的。
为什么要在类中使用静态呢?
把它们放入Entity中是有意义的。比如你有一条信息,你想要在所有Entity实例之间共享数据,或者将它实际存储在Entity类中是有意义的,因为它与Entity有关。
要组织好的代码,最好在这个类中创建一个静态变量,而不是一些静态的或全局的东西到处乱放?静态方法的工作方式与此类似。如果我让这个print方法变成静态,它会正常工作,因为你可以看到它指向 x 和 y,它们也是静态变量。
#include <iostream> struct Entity { static int x, y; static void Print() { std::cout << x << "," << y <<std::endl; } }; int Entity::x; int Entity::y; int main() { Entity e; Entity::x = 2; Entity::y = 10; Entity e1; Entity::x = 5; Entity::y = 8; Entity::Print(); Entity::Print(); std::cin.get(); }
它会打印出同样的东西,因为运行了两次相同的方法。在这个例子中我们甚至根本不需要类实例,因为我们所做的一切都是静态的。如果我们决定x,y是非静态的,事情就变了。
print方法仍然保持static,但静态方法不能访问非静态变量。
原因是静态方法没有类实例。本质上,你在类中写的每一个方法,每个静态方法总是获得当前类的一个实例作为参数。这就是类在幕后的实际工作方式。在类中你看不到这种东西,它们通过隐藏参数发挥作用。静态方法不会得到那个隐藏参数。静态方法与在类外部编写的方法相同。
static void Print(Entity e) { std::cout << e.x << "," << e.y << std::endl; }
如果把 Entity 实例去掉,这正是我们将static关键字添加到类方法时所做的。就会出错。因为它不知道您想要访问哪个 Entity 的 x 和 y,因为您没有给它一个Entity的引用。
23.Local Static in C++
这是在一个局部作用域。你可以在局部作用域中使用static来声明一个变量。局部静态会有更多的含义。
声明一个变量,我们需要考虑两种情况。这就是变量的生存期和变量的作用域。
生存期指的是变量实际存在的时间(在它被删除之前,它会在我们的内存中存在多久)。变量的作用域是指我们可以访问变量的范围。
如果在一个函数内部声明一个变量,例如,我们不能在其他的函数中访问它,因为我们声明的变量对于我们声明的函数是局部的。静态局部变量允许我们声明一个变量,它的生存期基本上相当于整个程序的生存期,然而它的作用范围被限制在这个函数内,但它和函数没有什么关系,即可以在任何作用域中声明这个。
即函数作用域中的static和类作用域中的static之间没有太大的区别,因为生存期实际上是相同的,唯一的区别是,在类作用域中,类中的任何东西都可以访问静态变量。
如果你在函数作用域中声明一个静态变量,那么它将是那个函数的局部变量,对类来说也是局部变量。
#include <iostream> void Function1() { int i = 0; i++; std::cout << i << std::endl; } int main() { Function1(); Function1(); Function1(); Function1(); Function1();
std::cin.get(); }
运行结果:

#include <iostream>
void Function1()
{
static int i = 0;
i++;
std::cout << i << std::endl;
}
int main()
{
Function1();
Function1();
Function1();
Function1();
Function1();
std::cin.get();
}
运行结果:

前面加上static,相当于到函数外面!!!
24.ENUMS in C++
枚举就是一个数值集合。
#include <iostream> #define LOG(x) std::cout << x << std::endl class Log { public: enum Level { LevelError = 0, LevelWarning, LevelInfo }; private: Level m_LogLevel = LevelInfo; public: void SetLevel(Level level) { m_LogLevel = level; } void Error(const char* message) { if (m_LogLevel >= LevelError) std::cout << "[ERROR]:" << message << std::endl; } void Warn(const char* message) { if (m_LogLevel >= LevelWarning) std::cout << "[WARNING]:" << message << std::endl; } void Info(const char* message) { if (m_LogLevel >= LevelInfo) std::cout << "[INFO]:" << message << std::endl; } }; int main() { Log log; log.SetLevel(Log::LevelError); log.Warn("Hello"); log.Error("Hello"); log.Info("Hello!"); std::cin.get(); }
25.Constructors in C++
构造函数是一种特殊类型的方法,这是一种每次你构造一个对象时都会调用的方法。它没有返回类型,并且它的名称必须和类相同。如果不指定构造函数,您仍然有一个构造函数,它只是以一个叫做默认构造函数的东西。它默认情况下已经为你准备好了。然而,这个构造函数什么也没有做。像Java等语言,数据基本类型,比如int和float,会自动初始化为0,但C++不是。您必须手动初始化所有基本类型。
带参数的构造函数。
这里也有一些方法可以删除构造函数,例如你有一个Write类,它只有静态的日志方法。我只想让人们像这样使用Write类,我不希望人们创建实例,有两种不同的解决方法。我们可以通过设置private来隐藏构造函数
#include <iostream> #define LOG(x) std::cout << x << std::endl class Entity { public: float X, Y; Entity() { X = 0.0f; Y = 0.0f; } Entity(float x, float y) { X = x; Y = y; } void Print() { std::cout << X << ", " << Y << std::endl; } }; class Log { private: Log(){} public: static void Write() { } }; int main() { Log::Write(); log l; //报错,因为不能访问构造器 Entity e(10.0f, 5.0f); e.Print(); std::cin.get(); }
C++可以为我们提供一个默认构造函数,然而我们可以告诉编译器,不,我不想要那个默认构造函数,我们可以简单地写 Log() = delete;
构造函数的主要用途时初始化该类。
26.Destructors in C++
析构函数。
构造函数时你创建一个新的实例对象时运行,而析构函数是你卸载变量等东西,并清理你使用过的内存。析构函数同样适用于栈和堆分配的对象。如果你使用new分配一个对象,当你调用delete时,析构函数会被调用。而如果只是一个栈对象,当作用域结束时,栈对象将被删除,这时,析构函数也会被调用。
为什么要写析构函数呢?
因为如果在构造函数中调用了特定的初始化代码,你可能想要在析构函数中,卸载或销毁所有这些东西。因为如果你不这样做,你可能会造成内存泄漏。
是在堆上分配的对象,如果您已经在堆上手动分配了任何类型的内存,那么您需要手动清理。如果在Entity类使用中或者构造中分配了内存。你可能会要在析构函数中删除它们(内存),因为当析构函数调用时,那个Entity实例对象消失了。但你可以这样调用,e.~Entity,就像它是任何其他函数一样,如果我运行代码,我们会调用析构函数。
#include <iostream> class Entity { public: float X, Y; Entity() { X = 0.0f; Y = 0.0f; std::cout << "Created Entity" << std::endl; } ~Entity() { std::cout << "Destroyed Entity!" << std::endl; } void Print() { std::cout << X << ", " << Y << std::endl; } }; void Function1() { Entity e; e.Print();
// e.~Entity();
} int main() { Function1(); std::cin.get(); }


27.Inheritance in C++
#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; Player player; player.Move(5, 5); player.X = 2; std::cin.get(); }
运行结果:
8
将 sizeof(Entity) 换成 sizeof(Player) ,运行结果为 16 。
28.Virtual Functions in C++
C++虚函数。虚函数允许我们在子类中重写方法。假设我们有两个类A和B,B是A派生出来的,也就是B是A的子类。如果我们在A类中创建一个方法,标记为virtual,我们可以在B类中重写那个方法,让它做其他的事情。
#include <iostream> class Entity { public: std::string GetName() { return "Entity"; } }; class Player :public Entity { private: std::string m_Name; public: Player(const std::string& name) : m_Name(name) { } std::string GetName() { return m_Name; } }; void PrintName(Entity* entity) { std::cout << entity->GetName() << std::endl; } int main() { Entity* e = new Entity(); //std::cout << e->GetName() << std::endl; PrintName(e); Player* p = new Player("ccx"); //std::cout << p->GetName() << std::endl; PrintName(p); //Entity* entity = p; //std::cout << entity->GetName() << std::endl; std::cin.get(); }
我们期望的是,这个 GetName 用于Entity,而这个GetName用于Player。运行结果:
Entity
Entity
为什么会得到错误结果?
发生这种情况的原因是,我们通常声明函数时,我们的方法在类内部(起作用)。然后当要调用方法的时候,会调用属于该类型的方法。。。
这就是虚函数出现的地方,虚函数引入了一种叫Dynamic Dispatch(动态联编)的东西,它通常通过v表(虚函数表)来实现编译。v表就是一个表,它包含基类中所有虚函数的映射,这样我们可以在它运行时,将它们映射到正确的覆写(override)函数。你只需要知道,如果你想覆写一个函数,必须将基类中的基函数标记为虚函数。

运行结果:

我们也可以做另一件事,是在C++11引入的,将覆写函数标记为关键字"override",写在这里,这不是必须的。但这让它更具有可读性,而且它还可以帮助我们预防bug的发生,比如拼写错误之类的,输成小写了。
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; } };
#include <iostream> class Entity { public: virtual std::string GetName() { return "Entity"; } //virtual可以告诉编译器,生成v表吧,为这个函数.这样如果它被重写了,你可以指向正确的函数 // }; 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(); //std::cout << e->GetName() << std::endl; PrintName(e); Player* p = new Player("ccx"); //std::cout << p->GetName() << std::endl; PrintName(p); //Entity* entity = p; //std::cout << entity->GetName() << std::endl; std::cin.get(); }
29.Interfaces in C++(Pure Virtual Functions)
纯虚函数运行我们在基类中定义一个没有实现的函数,然后强制子类去实现该函数。
纯虚函数必须被实现,才能创建这个类的实例。
#include <iostream> class Printable { public: virtual std::string GetClassName() = 0; }; class Entity:public Printable { public: virtual std::string GetName() { return "Entity"; } //virtual可以告诉编译器,生成v表吧,为这个函数.这样如果它被重写了,你可以指向正确的函数 std::string GetClassName() { return "Entity"; } }; class Player :public Entity, Printable { 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; } void Print1(Printable* obj) { std::cout << obj->GetClassName() << std::endl; } int main() { Entity* e = new Entity(); //std::cout << e->GetName() << std::endl; // PrintName(e); Player* p = new Player("ccx"); //std::cout << p->GetName() << std::endl; // PrintName(p); //Entity* entity = p; //std::cout << entity->GetName() << std::endl; std::cin.get(); }

浙公网安备 33010602011771号