Object Oriented Programming Basics – Arma Reforger

From Bohemia Interactive Community
Jump to navigation Jump to search
m (1 revision imported)
m (Text replacement - "overriden" to "overridden")
 
(9 intermediate revisions by 2 users not shown)
Line 12: Line 12:


A member variable is a variable scoped to that object instance.
A member variable is a variable scoped to that object instance.
It is usually {{hl|protected}} but can be {{hl|private}} (see {{HashLink|#Visibility}}); a public member is usually a bad practice - it is best to use {{HashLink|#Getter and Setter|Getters and Setters}}.
It is usually {{hl|protected}} but can be {{hl|private}} (see {{Link|#Visibility}}); a public member is usually a bad practice - it is best to use {{Link|#Getter and Setter|Getters and Setters}}.


See [[Arma Reforger:Scripting: Values]] for naming prefixes.
See [[Arma Reforger:Scripting: Values]] for naming prefixes.


<syntaxhighlight lang="cpp">
<enforce>
class MyClass
class MyClass
{
{
private int m_iValue = -1; // -1 is undefined value
protected int m_iValue = -1; // -1 is undefined value


void MyClass(int value)
void MyClass(int value)
Line 26: Line 26:
}
}
}
}
</syntaxhighlight>
</enforce>




Line 33: Line 33:
A method is an object's function or a class' (static) function - it can be seen as a "member function".
A method is an object's function or a class' (static) function - it can be seen as a "member function".


A method has a '''signature''', i.e a return type, a name and a parameters list - e.g: <syntaxhighlight lang="cpp">int GetNumber(bool canBeNegative)</syntaxhighlight>.
A method has a '''signature''', i.e a return type, a name and a parameters list - e.g: <enforce>int GetNumber(bool canBeNegative)</enforce>
 


Two methods can be named identically as long as they differ by their parameters.
Two methods can be named identically as long as they differ by their parameters.


<syntaxhighlight lang="cpp">
<enforce>
class MyClass
class MyClass
{
{
Line 80: Line 81:


int staticResult = MyClass.MyStaticMethod(); // without instance usage, staticResult is 33
int staticResult = MyClass.MyStaticMethod(); // without instance usage, staticResult is 33
</syntaxhighlight>
</enforce>


=== Getter and Setter ===
=== Getter and Setter ===
Line 88: Line 89:
A Setter describes a method that '''sets''' it.
A Setter describes a method that '''sets''' it.


<syntaxhighlight lang="cpp">
<enforce>
class MyClass
class MyClass
{
{
Line 96: Line 97:
class MyClass
class MyClass
{
{
private int m_iHealth;
protected int m_iHealth;


void MyClass() // constructor method
void MyClass() // constructor method
Line 118: Line 119:
class MyClass
class MyClass
{
{
private int m_iHeadHealth;
protected int m_iHeadHealth;
private int m_iBodyHealth;
protected int m_iBodyHealth;


void MyClass() // constructor method
void MyClass() // constructor method
Line 143: Line 144:
}
}
}
}
</syntaxhighlight>
</enforce>


=== Constructor ===
=== Constructor ===
Line 153: Line 154:


{{Feature|informative|
{{Feature|informative|
* The parent class constructor (see the {{HashLink|#Inheritance}} chapter below) is automatically called. Inheriting class can only ''add'' new arguments to those accepted by the base class constructor.
The parent class constructor (see the {{Link|#Inheritance}} chapter below) is automatically called.
An inheriting class can only '''add''' new arguments to those accepted by the base class constructor.
}}
}}


<syntaxhighlight lang="cpp">
<enforce>
class MyClass
class MyClass
{
{
Line 166: Line 168:
m_iValue = 50;
m_iValue = 50;
}
}
};
}


MyClass instance = new MyClass(); // instance.m_iValue = 50
MyClass instance = new MyClass(); // instance.m_iValue = 50
</syntaxhighlight>
</enforce>


<syntaxhighlight lang="cpp">
<enforce>
class MyClass
class MyClass
{
{
Line 178: Line 180:
void MyClass(int value)
void MyClass(int value)
{
{
PrintFormat("Instance created with value %1", m_iValue);
m_iValue = value;
m_iValue = value;
PrintFormat("Instance created with value %1", m_iValue);
}
}
};
}


MyClass instance = new MyClass(42); // prints "Instance created with value 42"
MyClass instance = new MyClass(42); // prints "Instance created with value 42"
MyClass instance = new MyClass(); // errors: argument is missing
MyClass instance = new MyClass(); // errors: argument is missing
</syntaxhighlight>
</enforce>


=== Destructor ===
=== Destructor ===
Line 191: Line 193:
A destructor method is a specific one: it is a method that is automatically called on object destruction, and exists only without arguments. There can be zero to one destructor.
A destructor method is a specific one: it is a method that is automatically called on object destruction, and exists only without arguments. There can be zero to one destructor.


A destructor is declared as a method having the same name as its class, starting with a tild.
A destructor is declared as a method having the same name as its class, starting with a tilde {{hl|~}}.


<syntaxhighlight lang="cpp">
<enforce>
class MyClass
class MyClass
{
{
Line 204: Line 206:
MyClass instance = new MyClass();
MyClass instance = new MyClass();
delete instance; // prints "I am being destroyed!";
delete instance; // prints "I am being destroyed!";
</syntaxhighlight>
</enforce>




Line 215: Line 217:
This is the default visibility - the member is accessible from inside as well as outside the object. As the default visibility, it does not need a keyword.
This is the default visibility - the member is accessible from inside as well as outside the object. As the default visibility, it does not need a keyword.


<syntaxhighlight lang="cpp">
<enforce noguess>
class ParentClass
class ParentClass
{
{
Line 234: Line 236:
ChildClass child = new ChildClass();
ChildClass child = new ChildClass();
child.Health = 10; // OK
child.Health = 10; // OK
</syntaxhighlight>
</enforce>


=== protected ===
=== protected ===
Line 243: Line 245:
{{Feature|important|It is important to keep visibility to {{hl|protected}} for modding purpose - an inherited class can then expand or reuse these methods.}}
{{Feature|important|It is important to keep visibility to {{hl|protected}} for modding purpose - an inherited class can then expand or reuse these methods.}}


<syntaxhighlight lang="cpp">
<enforce>
class ParentClass
class ParentClass
{
{
Line 264: Line 266:
child.m_iHealth = 10; // error: cannot access from the outside
child.m_iHealth = 10; // error: cannot access from the outside
child.SetHealth(10); // OK - the child can internally edit the inherited member
child.SetHealth(10); // OK - the child can internally edit the inherited member
</syntaxhighlight>
</enforce>


=== private ===
=== private ===
Line 272: Line 274:
It uses the {{hl|private}} keyword.
It uses the {{hl|private}} keyword.


<syntaxhighlight lang="cpp">
<enforce>
class ParentClass
class ParentClass
{
{
Line 289: Line 291:
parent.m_iHealth = 10; // error: cannot access from the outside
parent.m_iHealth = 10; // error: cannot access from the outside
parent.SetHealth(10); // error: the SetHealth method is a member of ChildClass, not ParentClass
parent.SetHealth(10); // error: the SetHealth method is a member of ChildClass, not ParentClass
</syntaxhighlight>
</enforce>




Line 296: Line 298:
Inheritance is the transmission of parent properties to a child class. Class inheritance is written with {{hl|:}}. A class can only inherit from '''one''' class.
Inheritance is the transmission of parent properties to a child class. Class inheritance is written with {{hl|:}}. A class can only inherit from '''one''' class.


<syntaxhighlight lang="cpp">
<enforce>
class ParentClass
class ParentClass
{
{
Line 319: Line 321:
ChildClass child = new ChildClass();
ChildClass child = new ChildClass();
child.ParentMethod(); // outputs "Parent Method"
child.ParentMethod(); // outputs "Parent Method"
child.ChildMethod(); // outputs "Child Method"</syntaxhighlight>
child.ChildMethod(); // outputs "Child Method"</enforce>


=== override ===
=== override ===


A '''non-private''' (protected or public) inherited method can be overridden thanks to the {{hl|override}} keyword:
A '''non-private''' (protected or public) inherited method can be overridden thanks to the {{hl|override}} keyword:
<syntaxhighlight lang="cpp">
<enforce>
class ParentClass
class ParentClass
{
{
Line 346: Line 348:
ChildClass child = new ChildClass();
ChildClass child = new ChildClass();
child.TheMethod(); // outputs "Child Method"
child.TheMethod(); // outputs "Child Method"
</syntaxhighlight>
</enforce>


=== super ===
=== super ===


An inherited object can call its parent's '''non-private''' (protected or public) method:
An inherited object can call its parent's '''non-private''' (protected or public) method:
<syntaxhighlight lang="cpp">
<enforce>
class ParentClass
class ParentClass
{
{
Line 374: Line 376:
ChildClass child = new ChildClass();
ChildClass child = new ChildClass();
child.TheMethod(); // outputs "ParentMethodA" then "Child MethodB"
child.TheMethod(); // outputs "ParentMethodA" then "Child MethodB"
</syntaxhighlight>
</enforce>


It can even call an overriden one:
It can even call an overridden one:
<syntaxhighlight lang="cpp">
<enforce>
class ParentClass
class ParentClass
{
{
Line 400: Line 402:
ChildClass child = new ChildClass();
ChildClass child = new ChildClass();
child.TheMethod(); // outputs "Parent Method" then "Child Method"
child.TheMethod(); // outputs "Parent Method" then "Child Method"
</syntaxhighlight>
</enforce>
 
 
== Casting ==
 
Casting is the act of "presenting" a value as another type. For example, if a class hierarchy is {{hl|Animal > Dog > Cocker}}, a dog is an animal, a cocker is a dog (that is an animal), but a dog is not especially a cocker.
 
<syntaxhighlight lang="cpp">
class Animal {}
class Dog : Animal {}
class Cocker : Dog {}
class Labrador : Dog {}
</syntaxhighlight>
 
=== Upcasting ===


Upcasting means seeing the class as one of its parents:
<syntaxhighlight lang="cpp">
void Method()
{
Cocker cocker = new Cocker();
Animal animal = cocker; // OK, as a cocker is an animal
string sentence = cocker; // error: cocker does -not- inherit from string
};
</syntaxhighlight>


=== Downcasting ===
== See Also ==


Downcasting means seeing a parent class as a specific child - this must be done by manually casting:
* [[Arma Reforger:Object Oriented Programming Advanced Usage|Object Oriented Programming Advanced Usage]]
<syntaxhighlight lang="cpp">
void Method(Dog dog)
{
Cocker cocker1 = dog; // error: "Dog" is too generic to be casted as Cocker - it could be e.g a Labrador
Cocker cocker2 = Cocker.Cast(dog); // OK: manual casting tells the code "the developer knows what he is doing"
// if 'dog' is not castable as a Cocker, null is returned - the code does not crash
 
Cocker cocker3;
cocker3.Cast(dog); // alternative method
}
</syntaxhighlight>
 
=== Manual Casting ===
 
<syntaxhighlight lang="cpp">
void Method()
{
float value1 = 4.9;
int value2 = value1; // value2 = 4 as integer casting -truncates- the value, not rounds it
string value3 = "result = " + (bool)value2; // "result = true", as a non-zero integer is true when casted to bool
}
</syntaxhighlight>




{{GameCategory|armaR|Modding|Tutorials|Scripting}}
{{GameCategory|armaR|Modding|Guidelines|Scripting}}

Latest revision as of 14:36, 9 November 2023

Class vs Object

A class is a definition of an object; it can be seen as the blueprint for an object's creation. An object is an instance of a class, as in an entity created following the class' specifics.

An object can hold values, known as member variables, and functions, known as methods.

A class can have variables and methods too, known as static variables and methods.


Member Variable

A member variable is a variable scoped to that object instance. It is usually protected but can be private (see Visibility); a public member is usually a bad practice - it is best to use Getters and Setters.

See Arma Reforger:Scripting: Values for naming prefixes.

class MyClass { protected int m_iValue = -1; // -1 is undefined value void MyClass(int value) { m_iValue = value; } }


Method

A method is an object's function or a class' (static) function - it can be seen as a "member function".

A method has a signature, i.e a return type, a name and a parameters list - e.g:

int GetNumber(bool canBeNegative)


Two methods can be named identically as long as they differ by their parameters.

class MyClass { void MyMethod() { Print("MyMethod without arguments"); } int MyMethod() // error: only the return type differs { return 42; } void MyMethod(int a) { Print("MyMethod with 1 int argument"); } void MyMethod(string a) { Print("MyMethod with 1 string argument"); } void MyMethod(bool a, string b) { Print("MyMethod with 1 bool, 1 string argument"); } static int MyStaticMethod() { return 33; } } MyClass instance = new MyClass(); // creates an instance of MyClass int result = instance.MyMethod(); // returns 42 instance.MyMethod(10); // prints "MyMethod with 1 int argument" instance.MyMethod("hello"); // prints "MyMethod with 1 string argument" instance.MyMethod(false, "hey"); // prints "MyMethod with 1 bool, 1 string argument" delete instance; // deletes instance - instance variable value is now null int staticResult = MyClass.MyStaticMethod(); // without instance usage, staticResult is 33

Getter and Setter

A Getter describes a method that gets the value of a property;

A Setter describes a method that sets it.

class MyClass { public int Health = 100; // bad } class MyClass { protected int m_iHealth; void MyClass() // constructor method { m_iHealth = 100; } int GetHealth() // health getter { return m_iHealth; } void SetHealth(int health) // health setter { m_iHealth = health; } } // because health could be implemented another way - and if it is, we do not want to have to go through old code to change calls class MyClass { protected int m_iHeadHealth; protected int m_iBodyHealth; void MyClass() // constructor method { m_iHeadHealth = 100; m_iBodyHealth = 100; } int GetHealth() // health getter { return m_iHeadHealth * 0.75 + m_iBodyHealth * 0.25; // head damage counts thrice } void SetHealth(int health) // health setter { if (health > 100 || health < 0) { return; } m_iHeadHealth = health * 0.75; m_iBodyHealth = health * 0.25; } }

Constructor

A constructor method is a specific one: it is a method that is automatically called on object instanciation, and can be with or without arguments.
There can be zero to one constructor.

A constructor is declared as a method having the same name as its class.

The parent class constructor (see the Inheritance chapter below) is automatically called. An inheriting class can only add new arguments to those accepted by the base class constructor.

class MyClass { int m_iValue; void MyClass() { PrintFormat("Instance created"); m_iValue = 50; } } MyClass instance = new MyClass(); // instance.m_iValue = 50

class MyClass { int m_iValue; void MyClass(int value) { PrintFormat("Instance created with value %1", m_iValue); m_iValue = value; } } MyClass instance = new MyClass(42); // prints "Instance created with value 42" MyClass instance = new MyClass(); // errors: argument is missing

Destructor

A destructor method is a specific one: it is a method that is automatically called on object destruction, and exists only without arguments. There can be zero to one destructor.

A destructor is declared as a method having the same name as its class, starting with a tilde ~.

class MyClass { void ~MyClass() { Print("I am being destroyed!"); } } MyClass instance = new MyClass(); delete instance; // prints "I am being destroyed!";


Visibility

Visibility is the accessibility of an object's or class' method/variable from the "outside" of that object/class.

public

This is the default visibility - the member is accessible from inside as well as outside the object. As the default visibility, it does not need a keyword.

class ParentClass { int Health = 100; // while a bad practice, a public member is PascalCase-named } class ChildClass : ParentClass { void ChildClass() { Health = 200; // stronger child } } ParentClass parent = new ParentClass(); parent.Health = 10; // OK ChildClass child = new ChildClass(); child.Health = 10; // OK

protected

This is a "hierarchy" visibility - the member is accessible from the object and objects of inheriting classes. It uses the protected keyword.

It is important to keep visibility to protected for modding purpose - an inherited class can then expand or reuse these methods.

class ParentClass { protected int m_iHealth = 100; } class ChildClass : ParentClass { void SetHealth(int health) { m_iHealth = health; } } ParentClass parent = new ParentClass(); parent.m_iHealth = 10; // error: cannot access from the outside parent.SetHealth(10); // error: the SetHealth method is a member of ChildClass, not ParentClass ChildClass child = new ChildClass(); child.m_iHealth = 10; // error: cannot access from the outside child.SetHealth(10); // OK - the child can internally edit the inherited member

private

This is the strictest visibility - only the object can access this member. This is useful to e.g cut code in smaller methods and not clutter the list of available methods on this object from the outside. It uses the private keyword.

class ParentClass { private int m_iHealth = 100; } class ChildClass : ParentClass { void SetHealth(int health) { m_iHealth = health; // error: ChildClass does not know m_iHealth } } ParentClass parent = new ParentClass(); parent.m_iHealth = 10; // error: cannot access from the outside parent.SetHealth(10); // error: the SetHealth method is a member of ChildClass, not ParentClass


Inheritance

Inheritance is the transmission of parent properties to a child class. Class inheritance is written with :. A class can only inherit from one class.

class ParentClass { void ParentMethod() { Print("Parent Method"); } } class ChildClass : ParentClass { void ChildMethod() { Print("Child Method"); } } ParentClass parent = new ParentClass(); parent.ParentMethod(); // outputs "Parent Method" parent.ChildMethod(); // error: ChildMethod is not a member of ParentClass, only ChildClass ChildClass child = new ChildClass(); child.ParentMethod(); // outputs "Parent Method" child.ChildMethod(); // outputs "Child Method"

override

A non-private (protected or public) inherited method can be overridden thanks to the override keyword:

class ParentClass { void TheMethod() { Print("Parent Method"); } } class ChildClass : ParentClass { override void TheMethod() { Print("Child Method"); } } ParentClass parent = new ParentClass(); parent.TheMethod(); // outputs "Parent Method" ChildClass child = new ChildClass(); child.TheMethod(); // outputs "Child Method"

super

An inherited object can call its parent's non-private (protected or public) method:

class ParentClass { void MethodA() { Print("Parent MethodA"); } } class ChildClass : ParentClass { void MethodB() { super.MethodA(); Print("Child MethodB"); } } ParentClass parent = new ParentClass(); parent.TheMethod(); // outputs "Parent MethodA" ChildClass child = new ChildClass(); child.TheMethod(); // outputs "ParentMethodA" then "Child MethodB"

It can even call an overridden one:

class ParentClass { void TheMethod() { Print("Parent Method"); } } class ChildClass : ParentClass { override void TheMethod() { super.TheMethod(); Print("Child Method"); } } ParentClass parent = new ParentClass(); parent.TheMethod(); // outputs "Parent Method" ChildClass child = new ChildClass(); child.TheMethod(); // outputs "Parent Method" then "Child Method"


See Also