[Cherno C + + Notes P21~P30]static, enumeration, constructor, destructor, inheritance, virtual function, interface, visibility

Series blog

[Cherno C + + Notes P1~P10] installation, linker, variable, function, header file
[Cherno C + + Notes P11~P20] judgment, loop, pointer, reference, class
[Cherno C + + Notes P21~P30] static, enumeration, constructor, destructor, inheritance, virtual function, interface, visibility

preface

This series of videos needs some foundation. It's best to have studied C.

Video link

P21 static in C + +
P22 static in C + + classes and structs
P23 Local Static in C + +
P24 C + + enumeration
P25 C + + constructor
P26 C + + destructor
P27 C + + inheritance
P28 C + + virtual function
P29 C + + interface (pure virtual function)
P30 C + + visibility

P21 static in C + +

static has two meanings in C + +, depending on the context

static outside a struct or class

Static outside the class / structure means that the symbol you declare as static, the link will only be inside, and it will only be visible to the translation unit you define it.
You can refer to the link notes in my first article.

static inside a struct or class

A static variable defined inside a class / struct means that the variable will actually share memory with all instances of the class.
Static variables have only one instance.
Similarly, no instance is passed to a static function.

P22 static in C + + classes and structs

If you mark a variable as static, there is only one instance of this variable in all instances of the class.

#include<iostream>

struct Entity
{
	int y;
	static int x;
};

int Entity::x;

int main() 
{
	Entity e1;
	e1.y = 1;
	e1.x = 1;

	Entity e2;
	e2.y = 2;
	e2.x = 2;
	
	std::cout << e1.y << std::endl;
	std::cout << e1.x << std::endl;
	std::cout << e2.y << std::endl;
	std::cout << e2.y << std::endl;

	std::cin.get();
}

As you can see, this output is

x in different Entity instances points to the same region.
We can write Entity::x = 2;

It should be noted that static members cannot reference non static members.
In the following example, the Print function does not know what x is and generates an error.

P23 Local Static in C + +

Local static allows us to create a variable whose lifetime is basically equivalent to the lifetime of the whole program, but the scope is limited to this domain.

#include<iostream>

void Func()
{
	static int i = 0;
	std::cout << i++ << std::endl;
}

int main() 
{
	Func();
	Func();
	Func();

	std::cin.get();
}


You can see that the life cycle of the variable marked static is basically the same as that of the program, and it is limited to Func().

Singleton class

A singleton class is a class that has only one instance

Creating a singleton class without using local statics
#include<iostream>

class Singleton
{
private:
	static Singleton* s_Instance;
public:
	static Singleton& Get() { return *s_Instance; };

	void Hello() 
	{
		std::cout << "Hello" << std::endl;
	}
};

Singleton* Singleton::s_Instance = nullptr;

int main() 
{
	Singleton::Get().Hello();

	std::cin.get();
}
Create a singleton class using local static
#include<iostream>

class Singleton
{

public:
	static Singleton& Get() 
	{
		static Singleton instance;
		return instance;
	};

	void Hello() 
	{
		std::cout << "Hello" << std::endl;
	}
};

int main() 
{
	Singleton::Get().Hello();

	std::cin.get();
}

 

P24 C + + enumeration

ENUM is the abbreviation of enumeration. It is a set of values. It can help us type a set of values, not just integers.
It's just a way to name values. It's very useful when you want to use integers to represent some states or some values.
In general, enumeration is an integer.

enum MyEnum
{
	A,B,C
};

When you do not specify a value for the element in the enumeration, the default first element is 0, the second is 1, and so on

Enumeration is a 32-bit integer by default. We can specify the data type to change the memory occupied by enumeration.

enum MyEnum : unsigned char
{
	A=2,B=4,C=6,D
};

Of course, you can't change to float type because float is not an integer.
When using, you can only select the elements in the enumeration, not define them yourself

P25 C + + constructor

A constructor is basically a special type of method that runs every time an object is instantiated
#include<iostream>


class Entity
{
public:

	float X, Y;

	void Print()
	{
		std::cout << X << Y << std::endl;
	}
};

int main() 
{

	Entity e;

	e.Print();

	std::cin.get();
}

In this example, although we instantiated the object, we did not initialize X and Y, so the output X and Y are the original values in memory.

class Entity
{
public:

	float X, Y;

	Entity()
	{
		X = 0;
		Y = 0;
	}

	void Print()
	{
		std::cout << X << Y << std::endl;
	}
};

int main() 
{

	Entity e;

	e.Print();

	std::cin.get();
}

 

Here, we add a constructor
Constructor has no return type. The function name must be the same as the class name

We can create a parameterized construct by means of method overloading

#include<iostream>


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;
	}
};

int main() 
{

	Entity e(1,2);

	e.Print();

	std::cin.get();
}
 

If there is no constructor written, there will be a parameterless construct Entity () {} by default.
We can use ` Entity() = delete`` To delete the default constructor in the class

The constructor ensures that you initialize all memory when you instantiate the object.

P26 C + + destructor

Destructors run when objects are destroyed.
Destructors are things that you unload variables and clean up the memory you've used.
Destructors apply to both stack and heap allocated objects.

#include<iostream>


class Entity
{
public:

	float X, Y;

	Entity()
	{
		X = 0.0f;
		Y = 0.0f;
	}

	Entity(float x, float y)
	{
		X = x;
		Y = y;
	}

	~Entity()
	{
		std::cout << "Destroyed Entity!" << std::endl;
	}

	void Print()
	{
		std::cout << X << Y << std::endl;
	}
};

void useFunc()
{
	Entity e(1, 2);

	e.Print();
}

int main() 
{

	useFunc();

	std::cin.get();
}
 

The difference in writing between destructors and constructors is~

P27 C + + inheritance

Object oriented programming is a huge programming paradigm. Inheritance between classes is one of its basic directions. It is one of the most powerful features that we can actually use.
Inheritance allows us to have a hierarchy of interrelated classes. It allows us to have a base class containing common functions, then separate from that base class and create subclasses from the original parent class.
The main reason why these classes, inheritance, etc. are so useful is to avoid code duplication.

#include<iostream>


class Entity
{
public:
	float X, Y;

	Entity()
	{
		X = 0;
		Y = 0;
	}

	void Move(float xa, float ya)
	{
		X += xa;
		Y += ya;

		std::cout << "X = " << X << ", Y = " << Y << std::endl;
	}
};

class Player : public Entity
{
public:
	const char* Name;

	Player()
	{
		Name = "a";
	}

	void PrintName()
	{
		std::cout << "My name is " << Name << std::endl;
	}
};

int main() 
{
	Player player;
	player.Move(5, 10);
	player.PrintName();

	

	std::cin.get();
}

 

Here, the Player class has all public properties in the Entity. It can access X, Y and Move in the Entity.
There is a concept called polymorphism
Basically, polymorphism is a single type, but it has the meaning of multiple types. Like the player here, it is not only a player, but also an Entity. We can use player wherever we want to use Entity, because player contains everything owned by Entity. Player is always a superset of Entity

P28 C + + virtual function

Virtual functions allow us to override methods in subclasses.

#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; }
};

int main() 
{
	Entity* e = new Entity();
	std::cout << e->GetName() << std::endl;

	Player* p = new Player("Player");
	std::cout << p->GetName() << std::endl;

	

	std::cin.get();
}

 

In the output results, we can see that two different results are output.

Here, if you use the concept of polymorphism, because Player is a subclass of Entity, you can use Player anywhere you can use Entity. Let's try it

#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; }
};

int main() 
{
	Entity* e = new Entity();
	std::cout << e->GetName() << std::endl;

	Player* p = new Player("Player");
	std::cout << p->GetName() << std::endl;

	Entity* entity = p;
	std::cout << entity->GetName() << std::endl;

	std::cin.get();
}

 


Here, although we assign the pointer p pointing to the Player type to the entity of the entity type, the third output result is still the result in the entity, not the Player result.
When we call a function in a class, we call it according to its type. Because entity is of type entity, we call the function in entity.
We want the third output to call the method in Player. What should we do?
In this case, we need to use virtual functions.
Virtual function introduces something called Dynamic Dispatch, which is usually compiled through v table (virtual function table).
The v table is a table that contains the mappings of all virtual functions in the base class, so that we can map them to the correct override function when it runs.
If you want to override a function, you must mark the function in the base class as a virtual function.

#include<iostream>


class Entity
{
public:
	virtual 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; }
};

int main() 
{
	Entity* e = new Entity();
	std::cout << e->GetName() << std::endl;

	Player* p = new Player("Player");
	std::cout << p->GetName() << std::endl;

	Entity* entity = p;
	std::cout << entity->GetName() << std::endl;

	std::cin.get();
}

 


We add a virtual before the function corresponding to the base class Entity, so that it is marked as a virtual function.

In c++11, override is introduced, which marks a function as an override function. Although it is not necessary, it improves the readability of the code.

std::string GetName() override { return m_Name; }

It can also help us reduce bugs. If the function name is written incorrectly and we can't find a function that can be overwritten (or intend to overwrite a non virtual function), it will report an error.

Creating the v table requires additional space to ensure that we can allocate the correct functions.
Invoking the v table also requires additional performance because the v table needs to be traversed.

P29 C + + interface (pure virtual function)

Pure virtual functions in C + + are similar to abstract methods or interfaces in other languages.
Pure virtual functions allow us to define an unimplemented function in the base class, and then force subclasses to implement the function.
We changed the function in the Entity class in the previous P to virtual std::string GetName() = 0;, The difference between it and virtual function is that it has no method body, but is written as = 0, which makes it a pure virtual function. At this time, the Entity class cannot be instantiated, and the Player class must override this function to be instantiated.

P30 C + + visibility

Visibility is a concept of object-oriented programming. It refers to how visible some members or methods of a class are actually (who can see them, who can call them, and who can use them).

Visibility is something that has no impact on the actual running mode of the program, and has no impact on similar things of program performance.

There are three basic visibility modifiers in C + +: private, protected, and public

In class, the default type is private, while in struct, the default type is public

private

Private means that * you can only * access these private members in this class
The * * sign is added because a keyword in C + + is friend, which allows a class or function to be called a friend (friend) of the class, and private members can be accessed from the class

protected

protected means that the Entity class and all subclasses in the hierarchy can also access these symbols.
In other words, a class and its subclasses can access the protected content, but they cannot be accessed in other functions and classes (such as main).

public

public means that everyone can access it.

Keywords: C++

Added by ahinkley on Sun, 19 Dec 2021 14:54:31 +0200