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();

}
View Code

 

 

 

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();

}

 

posted on 2022-03-29 10:20  ccxwyyjy  阅读(54)  评论(0)    收藏  举报